Merge changes Ifb450710,I61c4a132 * changes: Eliminated reconintra_mt.c Eliminated vp8mt_build_intra_predictors_mbuv_s
diff --git a/vp8/common/idctllm_test.cc b/vp8/common/idctllm_test.cc new file mode 100755 index 0000000..0f6ebe7 --- /dev/null +++ b/vp8/common/idctllm_test.cc
@@ -0,0 +1,31 @@ +/* + * Copyright (c) 2010 The WebM project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + + + extern "C" { + void vp8_short_idct4x4llm_c(short *input, unsigned char *pred_ptr, + int pred_stride, unsigned char *dst_ptr, + int dst_stride); +} + +#include "vpx_config.h" +#include "idctllm_test.h" +namespace +{ + +INSTANTIATE_TEST_CASE_P(C, IDCTTest, + ::testing::Values(vp8_short_idct4x4llm_c)); + +} // namespace + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +}
diff --git a/vp8/common/idctllm_test.h b/vp8/common/idctllm_test.h new file mode 100755 index 0000000..a6a694b --- /dev/null +++ b/vp8/common/idctllm_test.h
@@ -0,0 +1,113 @@ +/* + * Copyright (c) 2010 The WebM project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + + + #include "third_party/googletest/src/include/gtest/gtest.h" +typedef void (*idct_fn_t)(short *input, unsigned char *pred_ptr, + int pred_stride, unsigned char *dst_ptr, + int dst_stride); +namespace { +class IDCTTest : public ::testing::TestWithParam<idct_fn_t> +{ + protected: + virtual void SetUp() + { + int i; + + UUT = GetParam(); + memset(input, 0, sizeof(input)); + /* Set up guard blocks */ + for(i=0; i<256; i++) + output[i] = ((i&0xF)<4&&(i<64))?0:-1; + } + + idct_fn_t UUT; + short input[16]; + unsigned char output[256]; + unsigned char predict[256]; +}; + +TEST_P(IDCTTest, TestGuardBlocks) +{ + int i; + + for(i=0; i<256; i++) + if((i&0xF) < 4 && i<64) + EXPECT_EQ(0, output[i]) << i; + else + EXPECT_EQ(255, output[i]); +} + +TEST_P(IDCTTest, TestAllZeros) +{ + int i; + + UUT(input, output, 16, output, 16); + + for(i=0; i<256; i++) + if((i&0xF) < 4 && i<64) + EXPECT_EQ(0, output[i]) << "i==" << i; + else + EXPECT_EQ(255, output[i]) << "i==" << i; +} + +TEST_P(IDCTTest, TestAllOnes) +{ + int i; + + input[0] = 4; + UUT(input, output, 16, output, 16); + + for(i=0; i<256; i++) + if((i&0xF) < 4 && i<64) + EXPECT_EQ(1, output[i]) << "i==" << i; + else + EXPECT_EQ(255, output[i]) << "i==" << i; +} + +TEST_P(IDCTTest, TestAddOne) +{ + int i; + + for(i=0; i<256; i++) + predict[i] = i; + + input[0] = 4; + UUT(input, predict, 16, output, 16); + + for(i=0; i<256; i++) + if((i&0xF) < 4 && i<64) + EXPECT_EQ(i+1, output[i]) << "i==" << i; + else + EXPECT_EQ(255, output[i]) << "i==" << i; +} + +TEST_P(IDCTTest, TestWithData) +{ + int i; + + for(i=0; i<16; i++) + input[i] = i; + + UUT(input, output, 16, output, 16); + + for(i=0; i<256; i++) + if((i&0xF) > 3 || i>63) + EXPECT_EQ(255, output[i]) << "i==" << i; + else if(i == 0) + EXPECT_EQ(11, output[i]) << "i==" << i; + else if(i == 34) + EXPECT_EQ(1, output[i]) << "i==" << i; + else if(i == 2 || i == 17 || i == 32) + EXPECT_EQ(3, output[i]) << "i==" << i; + else + EXPECT_EQ(0, output[i]) << "i==" << i; +} +}
diff --git a/vp8/common/x86/idctllm_mmx_test.cc b/vp8/common/x86/idctllm_mmx_test.cc new file mode 100755 index 0000000..8c11533 --- /dev/null +++ b/vp8/common/x86/idctllm_mmx_test.cc
@@ -0,0 +1,31 @@ +/* + * Copyright (c) 2010 The WebM project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + + + extern "C" { + void vp8_short_idct4x4llm_mmx(short *input, unsigned char *pred_ptr, + int pred_stride, unsigned char *dst_ptr, + int dst_stride); +} + +#include "vp8/common/idctllm_test.h" + +namespace +{ + +INSTANTIATE_TEST_CASE_P(MMX, IDCTTest, + ::testing::Values(vp8_short_idct4x4llm_mmx)); + +} // namespace + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +}
diff --git a/vp8/decoder/decodframe.c b/vp8/decoder/decodframe.c index 670c36f..c368a4c 100644 --- a/vp8/decoder/decodframe.c +++ b/vp8/decoder/decodframe.c
@@ -849,6 +849,12 @@ } } } + else + { + /* No segmentation updates on this frame */ + xd->update_mb_segmentation_map = 0; + xd->update_mb_segmentation_data = 0; + } /* Read the loop filter level and type */ pc->filter_type = (LOOPFILTERTYPE) vp8_read_bit(bc);
diff --git a/vp8/decoder/onyxd_int.h b/vp8/decoder/onyxd_int.h index 989f68b..97cf0dc 100644 --- a/vp8/decoder/onyxd_int.h +++ b/vp8/decoder/onyxd_int.h
@@ -32,8 +32,6 @@ { MACROBLOCKD mbd; int mb_row; - int current_mb_col; - short *coef_ptr; } MB_ROW_DEC; typedef struct
diff --git a/vp8/decoder/threading.c b/vp8/decoder/threading.c index 4773a08..36db8fb 100644 --- a/vp8/decoder/threading.c +++ b/vp8/decoder/threading.c
@@ -305,6 +305,271 @@ xd->dst.uv_stride, xd->eobs+16); } +typedef void (*init_current_bc_fn_t)(VP8D_COMP *pbi, MACROBLOCKD *xd, + int start_mb_row, int mb_row, int num_part); + +static void init_current_bc(VP8D_COMP *pbi, MACROBLOCKD *xd, int start_mb_row, + int mb_row, int num_part) +{ + (void) start_mb_row; + + xd->current_bc = &pbi->mbc[mb_row%num_part]; +} + +static void init_current_bc_threads(VP8D_COMP *pbi, MACROBLOCKD *xd, + int start_mb_row, int mb_row, int num_part) +{ + (void) xd; + pbi->mb_row_di[start_mb_row - 1].mb_row = mb_row; + pbi->mb_row_di[start_mb_row - 1].mbd.current_bc = &pbi->mbc[mb_row%num_part]; +} + + +static void decode_mb_rows(VP8D_COMP *pbi, MACROBLOCKD *xd, int start_mb_row, + init_current_bc_fn_t init_current_bc_fn) +{ + volatile int *last_row_current_mb_col = NULL; + int mb_row; + VP8_COMMON *pc = &pbi->common; + int nsync = pbi->sync_range; + int num_part = 1 << pbi->common.multi_token_partition; + + + for (mb_row = start_mb_row; mb_row < pc->mb_rows; mb_row += (pbi->decoding_thread_count + 1)) + { + int i; + int recon_yoffset, recon_uvoffset; + int mb_col; + int ref_fb_idx = pc->lst_fb_idx; + int dst_fb_idx = pc->new_fb_idx; + int recon_y_stride = pc->yv12_fb[ref_fb_idx].y_stride; + int recon_uv_stride = pc->yv12_fb[ref_fb_idx].uv_stride; + + int filter_level; + loop_filter_info_n *lfi_n = &pc->lf_info; + + init_current_bc_fn(pbi, xd, start_mb_row, mb_row, num_part); + + if (mb_row > 0) + last_row_current_mb_col = &pbi->mt_current_mb_col[mb_row -1]; + + recon_yoffset = mb_row * recon_y_stride * 16; + recon_uvoffset = mb_row * recon_uv_stride * 8; + /* reset above block coeffs */ + + xd->above_context = pc->above_context; + vpx_memset(xd->left_context, 0, sizeof(ENTROPY_CONTEXT_PLANES)); + xd->up_available = (mb_row != 0); + + xd->mb_to_top_edge = -((mb_row * 16)) << 3; + xd->mb_to_bottom_edge = ((pc->mb_rows - 1 - mb_row) * 16) << 3; + + for (mb_col = 0; mb_col < pc->mb_cols; mb_col++) + { + if ( mb_row > 0 && (mb_col & (nsync-1)) == 0) + { + while (mb_col > (*last_row_current_mb_col - nsync) && *last_row_current_mb_col != pc->mb_cols - 1) + { + x86_pause_hint(); + thread_sleep(0); + } + } + + /* Distance of MB to the various image edges. + * These are specified to 8th pel as they are always + * compared to values that are in 1/8th pel units. + */ + xd->mb_to_left_edge = -((mb_col * 16) << 3); + xd->mb_to_right_edge = ((pc->mb_cols - 1 - mb_col) * 16) << 3; + + #if CONFIG_ERROR_CONCEALMENT + { + int corrupt_residual = + (!pbi->independent_partitions && + pbi->frame_corrupt_residual) || + vp8dx_bool_error(xd->current_bc); + if (pbi->ec_active && + (xd->mode_info_context->mbmi.ref_frame == + INTRA_FRAME) && + corrupt_residual) + { + /* We have an intra block with corrupt + * coefficients, better to conceal with an inter + * block. + * Interpolate MVs from neighboring MBs + * + * Note that for the first mb with corrupt + * residual in a frame, we might not discover + * that before decoding the residual. That + * happens after this check, and therefore no + * inter concealment will be done. + */ + vp8_interpolate_motion(xd, + mb_row, mb_col, + pc->mb_rows, pc->mb_cols, + pc->mode_info_stride); + } + } + #endif + + + xd->dst.y_buffer = pc->yv12_fb[dst_fb_idx].y_buffer + recon_yoffset; + xd->dst.u_buffer = pc->yv12_fb[dst_fb_idx].u_buffer + recon_uvoffset; + xd->dst.v_buffer = pc->yv12_fb[dst_fb_idx].v_buffer + recon_uvoffset; + + xd->left_available = (mb_col != 0); + + /* Select the appropriate reference frame for this MB */ + if (xd->mode_info_context->mbmi.ref_frame == LAST_FRAME) + ref_fb_idx = pc->lst_fb_idx; + else if (xd->mode_info_context->mbmi.ref_frame == GOLDEN_FRAME) + ref_fb_idx = pc->gld_fb_idx; + else + ref_fb_idx = pc->alt_fb_idx; + + xd->pre.y_buffer = pc->yv12_fb[ref_fb_idx].y_buffer + recon_yoffset; + xd->pre.u_buffer = pc->yv12_fb[ref_fb_idx].u_buffer + recon_uvoffset; + xd->pre.v_buffer = pc->yv12_fb[ref_fb_idx].v_buffer + recon_uvoffset; + + if (xd->mode_info_context->mbmi.ref_frame != + INTRA_FRAME) + { + /* propagate errors from reference frames */ + xd->corrupted |= pc->yv12_fb[ref_fb_idx].corrupted; + } + + decode_macroblock(pbi, xd, mb_row, mb_col); + + /* check if the boolean decoder has suffered an error */ + xd->corrupted |= vp8dx_bool_error(xd->current_bc); + + if (pbi->common.filter_level) + { + int skip_lf = (xd->mode_info_context->mbmi.mode != B_PRED && + xd->mode_info_context->mbmi.mode != SPLITMV && + xd->mode_info_context->mbmi.mb_skip_coeff); + + const int mode_index = lfi_n->mode_lf_lut[xd->mode_info_context->mbmi.mode]; + const int seg = xd->mode_info_context->mbmi.segment_id; + const int ref_frame = xd->mode_info_context->mbmi.ref_frame; + + filter_level = lfi_n->lvl[seg][ref_frame][mode_index]; + + if( mb_row != pc->mb_rows-1 ) + { + /* Save decoded MB last row data for next-row decoding */ + vpx_memcpy((pbi->mt_yabove_row[mb_row + 1] + 32 + mb_col*16), (xd->dst.y_buffer + 15 * recon_y_stride), 16); + vpx_memcpy((pbi->mt_uabove_row[mb_row + 1] + 16 + mb_col*8), (xd->dst.u_buffer + 7 * recon_uv_stride), 8); + vpx_memcpy((pbi->mt_vabove_row[mb_row + 1] + 16 + mb_col*8), (xd->dst.v_buffer + 7 * recon_uv_stride), 8); + } + + /* save left_col for next MB decoding */ + if(mb_col != pc->mb_cols-1) + { + MODE_INFO *next = xd->mode_info_context +1; + + if (next->mbmi.ref_frame == INTRA_FRAME) + { + for (i = 0; i < 16; i++) + pbi->mt_yleft_col[mb_row][i] = xd->dst.y_buffer [i* recon_y_stride + 15]; + for (i = 0; i < 8; i++) + { + pbi->mt_uleft_col[mb_row][i] = xd->dst.u_buffer [i* recon_uv_stride + 7]; + pbi->mt_vleft_col[mb_row][i] = xd->dst.v_buffer [i* recon_uv_stride + 7]; + } + } + } + + /* loopfilter on this macroblock. */ + if (filter_level) + { + if(pc->filter_type == NORMAL_LOOPFILTER) + { + loop_filter_info lfi; + FRAME_TYPE frame_type = pc->frame_type; + const int hev_index = lfi_n->hev_thr_lut[frame_type][filter_level]; + lfi.mblim = lfi_n->mblim[filter_level]; + lfi.blim = lfi_n->blim[filter_level]; + lfi.lim = lfi_n->lim[filter_level]; + lfi.hev_thr = lfi_n->hev_thr[hev_index]; + + if (mb_col > 0) + vp8_loop_filter_mbv + (xd->dst.y_buffer, xd->dst.u_buffer, xd->dst.v_buffer, recon_y_stride, recon_uv_stride, &lfi); + + if (!skip_lf) + vp8_loop_filter_bv + (xd->dst.y_buffer, xd->dst.u_buffer, xd->dst.v_buffer, recon_y_stride, recon_uv_stride, &lfi); + + /* don't apply across umv border */ + if (mb_row > 0) + vp8_loop_filter_mbh + (xd->dst.y_buffer, xd->dst.u_buffer, xd->dst.v_buffer, recon_y_stride, recon_uv_stride, &lfi); + + if (!skip_lf) + vp8_loop_filter_bh + (xd->dst.y_buffer, xd->dst.u_buffer, xd->dst.v_buffer, recon_y_stride, recon_uv_stride, &lfi); + } + else + { + if (mb_col > 0) + vp8_loop_filter_simple_mbv + (xd->dst.y_buffer, recon_y_stride, lfi_n->mblim[filter_level]); + + if (!skip_lf) + vp8_loop_filter_simple_bv + (xd->dst.y_buffer, recon_y_stride, lfi_n->blim[filter_level]); + + /* don't apply across umv border */ + if (mb_row > 0) + vp8_loop_filter_simple_mbh + (xd->dst.y_buffer, recon_y_stride, lfi_n->mblim[filter_level]); + + if (!skip_lf) + vp8_loop_filter_simple_bh + (xd->dst.y_buffer, recon_y_stride, lfi_n->blim[filter_level]); + } + } + + } + + recon_yoffset += 16; + recon_uvoffset += 8; + + ++xd->mode_info_context; /* next mb */ + + xd->above_context++; + + /*pbi->mb_row_di[ithread].current_mb_col = mb_col;*/ + pbi->mt_current_mb_col[mb_row] = mb_col; + } + + /* adjust to the next row of mbs */ + if (pbi->common.filter_level) + { + if(mb_row != pc->mb_rows-1) + { + int lasty = pc->yv12_fb[ref_fb_idx].y_width + VP8BORDERINPIXELS; + int lastuv = (pc->yv12_fb[ref_fb_idx].y_width>>1) + (VP8BORDERINPIXELS>>1); + + for (i = 0; i < 4; i++) + { + pbi->mt_yabove_row[mb_row +1][lasty + i] = pbi->mt_yabove_row[mb_row +1][lasty -1]; + pbi->mt_uabove_row[mb_row +1][lastuv + i] = pbi->mt_uabove_row[mb_row +1][lastuv -1]; + pbi->mt_vabove_row[mb_row +1][lastuv + i] = pbi->mt_vabove_row[mb_row +1][lastuv -1]; + } + } + } else + vp8_extend_mb_row(&pc->yv12_fb[dst_fb_idx], xd->dst.y_buffer + 16, xd->dst.u_buffer + 8, xd->dst.v_buffer + 8); + + ++xd->mode_info_context; /* skip prediction column */ + + /* since we have multithread */ + xd->mode_info_context += xd->mode_info_stride * pbi->decoding_thread_count; + } +} + + static THREAD_FUNCTION thread_decoding_proc(void *p_data) { int ithread = ((DECODETHREAD_DATA *)p_data)->ithread; @@ -324,251 +589,18 @@ break; else { - VP8_COMMON *pc = &pbi->common; MACROBLOCKD *xd = &mbrd->mbd; - int mb_row; - int num_part = 1 << pbi->common.multi_token_partition; - volatile int *last_row_current_mb_col; - int nsync = pbi->sync_range; + xd->left_context = &mb_row_left_context; - for (mb_row = ithread+1; mb_row < pc->mb_rows; mb_row += (pbi->decoding_thread_count + 1)) - { - int i; - int recon_yoffset, recon_uvoffset; - int mb_col; - int ref_fb_idx = pc->lst_fb_idx; - int dst_fb_idx = pc->new_fb_idx; - int recon_y_stride = pc->yv12_fb[ref_fb_idx].y_stride; - int recon_uv_stride = pc->yv12_fb[ref_fb_idx].uv_stride; - - int filter_level; - loop_filter_info_n *lfi_n = &pc->lf_info; - - pbi->mb_row_di[ithread].mb_row = mb_row; - pbi->mb_row_di[ithread].mbd.current_bc = &pbi->mbc[mb_row%num_part]; - - last_row_current_mb_col = &pbi->mt_current_mb_col[mb_row -1]; - - recon_yoffset = mb_row * recon_y_stride * 16; - recon_uvoffset = mb_row * recon_uv_stride * 8; - /* reset above block coeffs */ - - xd->above_context = pc->above_context; - xd->left_context = &mb_row_left_context; - vpx_memset(&mb_row_left_context, 0, sizeof(mb_row_left_context)); - xd->up_available = (mb_row != 0); - - xd->mb_to_top_edge = -((mb_row * 16)) << 3; - xd->mb_to_bottom_edge = ((pc->mb_rows - 1 - mb_row) * 16) << 3; - - for (mb_col = 0; mb_col < pc->mb_cols; mb_col++) - { - if ((mb_col & (nsync-1)) == 0) - { - while (mb_col > (*last_row_current_mb_col - nsync) && *last_row_current_mb_col != pc->mb_cols - 1) - { - x86_pause_hint(); - thread_sleep(0); - } - } - - /* Distance of MB to the various image edges. - * These are specified to 8th pel as they are always - * compared to values that are in 1/8th pel units. - */ - xd->mb_to_left_edge = -((mb_col * 16) << 3); - xd->mb_to_right_edge = ((pc->mb_cols - 1 - mb_col) * 16) << 3; - -#if CONFIG_ERROR_CONCEALMENT - { - int corrupt_residual = - (!pbi->independent_partitions && - pbi->frame_corrupt_residual) || - vp8dx_bool_error(xd->current_bc); - if (pbi->ec_active && - (xd->mode_info_context->mbmi.ref_frame == - INTRA_FRAME) && - corrupt_residual) - { - /* We have an intra block with corrupt - * coefficients, better to conceal with an inter - * block. - * Interpolate MVs from neighboring MBs - * - * Note that for the first mb with corrupt - * residual in a frame, we might not discover - * that before decoding the residual. That - * happens after this check, and therefore no - * inter concealment will be done. - */ - vp8_interpolate_motion(xd, - mb_row, mb_col, - pc->mb_rows, pc->mb_cols, - pc->mode_info_stride); - } - } -#endif - - - xd->dst.y_buffer = pc->yv12_fb[dst_fb_idx].y_buffer + recon_yoffset; - xd->dst.u_buffer = pc->yv12_fb[dst_fb_idx].u_buffer + recon_uvoffset; - xd->dst.v_buffer = pc->yv12_fb[dst_fb_idx].v_buffer + recon_uvoffset; - - xd->left_available = (mb_col != 0); - - /* Select the appropriate reference frame for this MB */ - if (xd->mode_info_context->mbmi.ref_frame == LAST_FRAME) - ref_fb_idx = pc->lst_fb_idx; - else if (xd->mode_info_context->mbmi.ref_frame == GOLDEN_FRAME) - ref_fb_idx = pc->gld_fb_idx; - else - ref_fb_idx = pc->alt_fb_idx; - - xd->pre.y_buffer = pc->yv12_fb[ref_fb_idx].y_buffer + recon_yoffset; - xd->pre.u_buffer = pc->yv12_fb[ref_fb_idx].u_buffer + recon_uvoffset; - xd->pre.v_buffer = pc->yv12_fb[ref_fb_idx].v_buffer + recon_uvoffset; - - if (xd->mode_info_context->mbmi.ref_frame != - INTRA_FRAME) - { - /* propagate errors from reference frames */ - xd->corrupted |= pc->yv12_fb[ref_fb_idx].corrupted; - } - - decode_macroblock(pbi, xd, mb_row, mb_col); - - /* check if the boolean decoder has suffered an error */ - xd->corrupted |= vp8dx_bool_error(xd->current_bc); - - if (pbi->common.filter_level) - { - int skip_lf = (xd->mode_info_context->mbmi.mode != B_PRED && - xd->mode_info_context->mbmi.mode != SPLITMV && - xd->mode_info_context->mbmi.mb_skip_coeff); - - const int mode_index = lfi_n->mode_lf_lut[xd->mode_info_context->mbmi.mode]; - const int seg = xd->mode_info_context->mbmi.segment_id; - const int ref_frame = xd->mode_info_context->mbmi.ref_frame; - - filter_level = lfi_n->lvl[seg][ref_frame][mode_index]; - - if( mb_row != pc->mb_rows-1 ) - { - /* Save decoded MB last row data for next-row decoding */ - vpx_memcpy((pbi->mt_yabove_row[mb_row + 1] + 32 + mb_col*16), (xd->dst.y_buffer + 15 * recon_y_stride), 16); - vpx_memcpy((pbi->mt_uabove_row[mb_row + 1] + 16 + mb_col*8), (xd->dst.u_buffer + 7 * recon_uv_stride), 8); - vpx_memcpy((pbi->mt_vabove_row[mb_row + 1] + 16 + mb_col*8), (xd->dst.v_buffer + 7 * recon_uv_stride), 8); - } - - /* save left_col for next MB decoding */ - if(mb_col != pc->mb_cols-1) - { - MODE_INFO *next = xd->mode_info_context +1; - - if (next->mbmi.ref_frame == INTRA_FRAME) - { - for (i = 0; i < 16; i++) - pbi->mt_yleft_col[mb_row][i] = xd->dst.y_buffer [i* recon_y_stride + 15]; - for (i = 0; i < 8; i++) - { - pbi->mt_uleft_col[mb_row][i] = xd->dst.u_buffer [i* recon_uv_stride + 7]; - pbi->mt_vleft_col[mb_row][i] = xd->dst.v_buffer [i* recon_uv_stride + 7]; - } - } - } - - /* loopfilter on this macroblock. */ - if (filter_level) - { - if(pc->filter_type == NORMAL_LOOPFILTER) - { - loop_filter_info lfi; - FRAME_TYPE frame_type = pc->frame_type; - const int hev_index = lfi_n->hev_thr_lut[frame_type][filter_level]; - lfi.mblim = lfi_n->mblim[filter_level]; - lfi.blim = lfi_n->blim[filter_level]; - lfi.lim = lfi_n->lim[filter_level]; - lfi.hev_thr = lfi_n->hev_thr[hev_index]; - - if (mb_col > 0) - vp8_loop_filter_mbv - (xd->dst.y_buffer, xd->dst.u_buffer, xd->dst.v_buffer, recon_y_stride, recon_uv_stride, &lfi); - - if (!skip_lf) - vp8_loop_filter_bv - (xd->dst.y_buffer, xd->dst.u_buffer, xd->dst.v_buffer, recon_y_stride, recon_uv_stride, &lfi); - - /* don't apply across umv border */ - if (mb_row > 0) - vp8_loop_filter_mbh - (xd->dst.y_buffer, xd->dst.u_buffer, xd->dst.v_buffer, recon_y_stride, recon_uv_stride, &lfi); - - if (!skip_lf) - vp8_loop_filter_bh - (xd->dst.y_buffer, xd->dst.u_buffer, xd->dst.v_buffer, recon_y_stride, recon_uv_stride, &lfi); - } - else - { - if (mb_col > 0) - vp8_loop_filter_simple_mbv - (xd->dst.y_buffer, recon_y_stride, lfi_n->mblim[filter_level]); - - if (!skip_lf) - vp8_loop_filter_simple_bv - (xd->dst.y_buffer, recon_y_stride, lfi_n->blim[filter_level]); - - /* don't apply across umv border */ - if (mb_row > 0) - vp8_loop_filter_simple_mbh - (xd->dst.y_buffer, recon_y_stride, lfi_n->mblim[filter_level]); - - if (!skip_lf) - vp8_loop_filter_simple_bh - (xd->dst.y_buffer, recon_y_stride, lfi_n->blim[filter_level]); - } - } - - } - - recon_yoffset += 16; - recon_uvoffset += 8; - - ++xd->mode_info_context; /* next mb */ - - xd->above_context++; - - /*pbi->mb_row_di[ithread].current_mb_col = mb_col;*/ - pbi->mt_current_mb_col[mb_row] = mb_col; - } - - /* adjust to the next row of mbs */ - if (pbi->common.filter_level) - { - if(mb_row != pc->mb_rows-1) - { - int lasty = pc->yv12_fb[ref_fb_idx].y_width + VP8BORDERINPIXELS; - int lastuv = (pc->yv12_fb[ref_fb_idx].y_width>>1) + (VP8BORDERINPIXELS>>1); - - for (i = 0; i < 4; i++) - { - pbi->mt_yabove_row[mb_row +1][lasty + i] = pbi->mt_yabove_row[mb_row +1][lasty -1]; - pbi->mt_uabove_row[mb_row +1][lastuv + i] = pbi->mt_uabove_row[mb_row +1][lastuv -1]; - pbi->mt_vabove_row[mb_row +1][lastuv + i] = pbi->mt_vabove_row[mb_row +1][lastuv -1]; - } - } - } else - vp8_extend_mb_row(&pc->yv12_fb[dst_fb_idx], xd->dst.y_buffer + 16, xd->dst.u_buffer + 8, xd->dst.v_buffer + 8); - - ++xd->mode_info_context; /* skip prediction column */ - - /* since we have multithread */ - xd->mode_info_context += xd->mode_info_stride * pbi->decoding_thread_count; - } + decode_mb_rows(pbi, xd, ithread+1, init_current_bc_threads); } } + /* add this to each frame */ - if ((mbrd->mb_row == pbi->common.mb_rows-1) || ((mbrd->mb_row == pbi->common.mb_rows-2) && (pbi->common.mb_rows % (pbi->decoding_thread_count+1))==1)) + if ((mbrd->mb_row == pbi->common.mb_rows-1) || + ((mbrd->mb_row == pbi->common.mb_rows-2) && + (pbi->common.mb_rows % (pbi->decoding_thread_count+1))==1)) { /*SetEvent(pbi->h_event_end_decoding);*/ sem_post(&pbi->h_event_end_decoding); @@ -795,16 +827,10 @@ void vp8mt_decode_mb_rows( VP8D_COMP *pbi, MACROBLOCKD *xd) { - int mb_row; VP8_COMMON *pc = &pbi->common; - - int num_part = 1 << pbi->common.multi_token_partition; int i; - volatile int *last_row_current_mb_col = NULL; - int nsync = pbi->sync_range; int filter_level = pc->filter_level; - loop_filter_info_n *lfi_n = &pc->lf_info; if (filter_level) { @@ -837,229 +863,7 @@ for (i = 0; i < pbi->decoding_thread_count; i++) sem_post(&pbi->h_event_start_decoding[i]); - for (mb_row = 0; mb_row < pc->mb_rows; mb_row += (pbi->decoding_thread_count + 1)) - { - xd->current_bc = &pbi->mbc[mb_row%num_part]; - - /* vp8_decode_mb_row(pbi, pc, mb_row, xd); */ - { - int i; - int recon_yoffset, recon_uvoffset; - int mb_col; - int ref_fb_idx = pc->lst_fb_idx; - int dst_fb_idx = pc->new_fb_idx; - int recon_y_stride = pc->yv12_fb[ref_fb_idx].y_stride; - int recon_uv_stride = pc->yv12_fb[ref_fb_idx].uv_stride; - - /* volatile int *last_row_current_mb_col = NULL; */ - if (mb_row > 0) - last_row_current_mb_col = &pbi->mt_current_mb_col[mb_row -1]; - - vpx_memset(&pc->left_context, 0, sizeof(pc->left_context)); - recon_yoffset = mb_row * recon_y_stride * 16; - recon_uvoffset = mb_row * recon_uv_stride * 8; - /* reset above block coeffs */ - - xd->above_context = pc->above_context; - xd->up_available = (mb_row != 0); - - xd->mb_to_top_edge = -((mb_row * 16)) << 3; - xd->mb_to_bottom_edge = ((pc->mb_rows - 1 - mb_row) * 16) << 3; - - for (mb_col = 0; mb_col < pc->mb_cols; mb_col++) - { - if ( mb_row > 0 && (mb_col & (nsync-1)) == 0){ - while (mb_col > (*last_row_current_mb_col - nsync) && *last_row_current_mb_col != pc->mb_cols - 1) - { - x86_pause_hint(); - thread_sleep(0); - } - } - - /* Distance of MB to the various image edges. - * These are specified to 8th pel as they are always compared to - * values that are in 1/8th pel units. - */ - xd->mb_to_left_edge = -((mb_col * 16) << 3); - xd->mb_to_right_edge = ((pc->mb_cols - 1 - mb_col) * 16) << 3; - -#if CONFIG_ERROR_CONCEALMENT - { - int corrupt_residual = (!pbi->independent_partitions && - pbi->frame_corrupt_residual) || - vp8dx_bool_error(xd->current_bc); - if (pbi->ec_active && - (xd->mode_info_context->mbmi.ref_frame == INTRA_FRAME) && - corrupt_residual) - { - /* We have an intra block with corrupt coefficients, - * better to conceal with an inter block. Interpolate - * MVs from neighboring MBs - * - * Note that for the first mb with corrupt residual in a - * frame, we might not discover that before decoding the - * residual. That happens after this check, and - * therefore no inter concealment will be done. - */ - vp8_interpolate_motion(xd, - mb_row, mb_col, - pc->mb_rows, pc->mb_cols, - pc->mode_info_stride); - } - } -#endif - - - xd->dst.y_buffer = pc->yv12_fb[dst_fb_idx].y_buffer + recon_yoffset; - xd->dst.u_buffer = pc->yv12_fb[dst_fb_idx].u_buffer + recon_uvoffset; - xd->dst.v_buffer = pc->yv12_fb[dst_fb_idx].v_buffer + recon_uvoffset; - - xd->left_available = (mb_col != 0); - - /* Select the appropriate reference frame for this MB */ - if (xd->mode_info_context->mbmi.ref_frame == LAST_FRAME) - ref_fb_idx = pc->lst_fb_idx; - else if (xd->mode_info_context->mbmi.ref_frame == GOLDEN_FRAME) - ref_fb_idx = pc->gld_fb_idx; - else - ref_fb_idx = pc->alt_fb_idx; - - xd->pre.y_buffer = pc->yv12_fb[ref_fb_idx].y_buffer + recon_yoffset; - xd->pre.u_buffer = pc->yv12_fb[ref_fb_idx].u_buffer + recon_uvoffset; - xd->pre.v_buffer = pc->yv12_fb[ref_fb_idx].v_buffer + recon_uvoffset; - - if (xd->mode_info_context->mbmi.ref_frame != INTRA_FRAME) - { - /* propagate errors from reference frames */ - xd->corrupted |= pc->yv12_fb[ref_fb_idx].corrupted; - } - - decode_macroblock(pbi, xd, mb_row, mb_col); - - /* check if the boolean decoder has suffered an error */ - xd->corrupted |= vp8dx_bool_error(xd->current_bc); - - if (pbi->common.filter_level) - { - int skip_lf = (xd->mode_info_context->mbmi.mode != B_PRED && - xd->mode_info_context->mbmi.mode != SPLITMV && - xd->mode_info_context->mbmi.mb_skip_coeff); - - const int mode_index = lfi_n->mode_lf_lut[xd->mode_info_context->mbmi.mode]; - const int seg = xd->mode_info_context->mbmi.segment_id; - const int ref_frame = xd->mode_info_context->mbmi.ref_frame; - - filter_level = lfi_n->lvl[seg][ref_frame][mode_index]; - - /* Save decoded MB last row data for next-row decoding */ - if(mb_row != pc->mb_rows-1) - { - vpx_memcpy((pbi->mt_yabove_row[mb_row +1] + 32 + mb_col*16), (xd->dst.y_buffer + 15 * recon_y_stride), 16); - vpx_memcpy((pbi->mt_uabove_row[mb_row +1] + 16 + mb_col*8), (xd->dst.u_buffer + 7 * recon_uv_stride), 8); - vpx_memcpy((pbi->mt_vabove_row[mb_row +1] + 16 + mb_col*8), (xd->dst.v_buffer + 7 * recon_uv_stride), 8); - } - - /* save left_col for next MB decoding */ - if(mb_col != pc->mb_cols-1) - { - MODE_INFO *next = xd->mode_info_context +1; - - if (next->mbmi.ref_frame == INTRA_FRAME) - { - for (i = 0; i < 16; i++) - pbi->mt_yleft_col[mb_row][i] = xd->dst.y_buffer [i* recon_y_stride + 15]; - for (i = 0; i < 8; i++) - { - pbi->mt_uleft_col[mb_row][i] = xd->dst.u_buffer [i* recon_uv_stride + 7]; - pbi->mt_vleft_col[mb_row][i] = xd->dst.v_buffer [i* recon_uv_stride + 7]; - } - } - } - - /* loopfilter on this macroblock. */ - if (filter_level) - { - if(pc->filter_type == NORMAL_LOOPFILTER) - { - loop_filter_info lfi; - FRAME_TYPE frame_type = pc->frame_type; - const int hev_index = lfi_n->hev_thr_lut[frame_type][filter_level]; - lfi.mblim = lfi_n->mblim[filter_level]; - lfi.blim = lfi_n->blim[filter_level]; - lfi.lim = lfi_n->lim[filter_level]; - lfi.hev_thr = lfi_n->hev_thr[hev_index]; - - if (mb_col > 0) - vp8_loop_filter_mbv - (xd->dst.y_buffer, xd->dst.u_buffer, xd->dst.v_buffer, recon_y_stride, recon_uv_stride, &lfi); - - if (!skip_lf) - vp8_loop_filter_bv - (xd->dst.y_buffer, xd->dst.u_buffer, xd->dst.v_buffer, recon_y_stride, recon_uv_stride, &lfi); - - /* don't apply across umv border */ - if (mb_row > 0) - vp8_loop_filter_mbh - (xd->dst.y_buffer, xd->dst.u_buffer, xd->dst.v_buffer, recon_y_stride, recon_uv_stride, &lfi); - - if (!skip_lf) - vp8_loop_filter_bh - (xd->dst.y_buffer, xd->dst.u_buffer, xd->dst.v_buffer, recon_y_stride, recon_uv_stride, &lfi); - } - else - { - if (mb_col > 0) - vp8_loop_filter_simple_mbv - (xd->dst.y_buffer, recon_y_stride, lfi_n->mblim[filter_level]); - - if (!skip_lf) - vp8_loop_filter_simple_bv - (xd->dst.y_buffer, recon_y_stride, lfi_n->blim[filter_level]); - - /* don't apply across umv border */ - if (mb_row > 0) - vp8_loop_filter_simple_mbh - (xd->dst.y_buffer, recon_y_stride, lfi_n->mblim[filter_level]); - - if (!skip_lf) - vp8_loop_filter_simple_bh - (xd->dst.y_buffer, recon_y_stride, lfi_n->blim[filter_level]); - } - } - - } - recon_yoffset += 16; - recon_uvoffset += 8; - - ++xd->mode_info_context; /* next mb */ - - xd->above_context++; - - pbi->mt_current_mb_col[mb_row] = mb_col; - } - - /* adjust to the next row of mbs */ - if (pbi->common.filter_level) - { - if(mb_row != pc->mb_rows-1) - { - int lasty = pc->yv12_fb[ref_fb_idx].y_width + VP8BORDERINPIXELS; - int lastuv = (pc->yv12_fb[ref_fb_idx].y_width>>1) + (VP8BORDERINPIXELS>>1); - - for (i = 0; i < 4; i++) - { - pbi->mt_yabove_row[mb_row +1][lasty + i] = pbi->mt_yabove_row[mb_row +1][lasty -1]; - pbi->mt_uabove_row[mb_row +1][lastuv + i] = pbi->mt_uabove_row[mb_row +1][lastuv -1]; - pbi->mt_vabove_row[mb_row +1][lastuv + i] = pbi->mt_vabove_row[mb_row +1][lastuv -1]; - } - } - }else - vp8_extend_mb_row(&pc->yv12_fb[dst_fb_idx], xd->dst.y_buffer + 16, xd->dst.u_buffer + 8, xd->dst.v_buffer + 8); - - ++xd->mode_info_context; /* skip prediction column */ - } - xd->mode_info_context += xd->mode_info_stride * pbi->decoding_thread_count; - } + decode_mb_rows(pbi, xd, 0, init_current_bc); sem_wait(&pbi->h_event_end_decoding); /* add back for each frame */ }
diff --git a/vp8/encoder/bitstream.c b/vp8/encoder/bitstream.c index 0bb5173..f26b4e4 100644 --- a/vp8/encoder/bitstream.c +++ b/vp8/encoder/bitstream.c
@@ -382,219 +382,23 @@ int i; unsigned char *ptr = cx_data; unsigned char *ptr_end = cx_data_end; - unsigned int shift; - vp8_writer *w; - ptr = cx_data; + vp8_writer * w; for (i = 0; i < num_part; i++) { + int mb_row; + w = cpi->bc + i + 1; + vp8_start_encode(w, ptr, ptr_end); + + for (mb_row = i; mb_row < cpi->common.mb_rows; mb_row += num_part) { - unsigned int split; - int count = w->count; - unsigned int range = w->range; - unsigned int lowvalue = w->lowvalue; - int mb_row; + const TOKENEXTRA *p = cpi->tplist[mb_row].start; + const TOKENEXTRA *stop = cpi->tplist[mb_row].stop; + int tokens = stop - p; - for (mb_row = i; mb_row < cpi->common.mb_rows; mb_row += num_part) - { - TOKENEXTRA *p = cpi->tplist[mb_row].start; - TOKENEXTRA *stop = cpi->tplist[mb_row].stop; - - while (p < stop) - { - const int t = p->Token; - vp8_token *const a = vp8_coef_encodings + t; - const vp8_extra_bit_struct *const b = vp8_extra_bits + t; - int i = 0; - const unsigned char *pp = p->context_tree; - int v = a->value; - int n = a->Len; - - if (p->skip_eob_node) - { - n--; - i = 2; - } - - do - { - const int bb = (v >> --n) & 1; - split = 1 + (((range - 1) * pp[i>>1]) >> 8); - i = vp8_coef_tree[i+bb]; - - if (bb) - { - lowvalue += split; - range = range - split; - } - else - { - range = split; - } - - shift = vp8_norm[range]; - range <<= shift; - count += shift; - - if (count >= 0) - { - int offset = shift - count; - - if ((lowvalue << (offset - 1)) & 0x80000000) - { - int x = w->pos - 1; - - while (x >= 0 && w->buffer[x] == 0xff) - { - w->buffer[x] = (unsigned char)0; - x--; - } - - w->buffer[x] += 1; - } - - validate_buffer(w->buffer + w->pos, - 1, - cx_data_end, - &cpi->common.error); - - w->buffer[w->pos++] = (lowvalue >> (24 - offset)); - - lowvalue <<= offset; - shift = count; - lowvalue &= 0xffffff; - count -= 8 ; - } - - lowvalue <<= shift; - } - while (n); - - - if (b->base_val) - { - const int e = p->Extra, L = b->Len; - - if (L) - { - const unsigned char *pp = b->prob; - int v = e >> 1; - int n = L; /* number of bits in v, assumed nonzero */ - int i = 0; - - do - { - const int bb = (v >> --n) & 1; - split = 1 + (((range - 1) * pp[i>>1]) >> 8); - i = b->tree[i+bb]; - - if (bb) - { - lowvalue += split; - range = range - split; - } - else - { - range = split; - } - - shift = vp8_norm[range]; - range <<= shift; - count += shift; - - if (count >= 0) - { - int offset = shift - count; - - if ((lowvalue << (offset - 1)) & 0x80000000) - { - int x = w->pos - 1; - - while (x >= 0 && w->buffer[x] == 0xff) - { - w->buffer[x] = (unsigned char)0; - x--; - } - - w->buffer[x] += 1; - } - - validate_buffer(w->buffer + w->pos, - 1, - cx_data_end, - &cpi->common.error); - - w->buffer[w->pos++] = - (lowvalue >> (24 - offset)); - - lowvalue <<= offset; - shift = count; - lowvalue &= 0xffffff; - count -= 8 ; - } - - lowvalue <<= shift; - } - while (n); - } - - { - split = (range + 1) >> 1; - - if (e & 1) - { - lowvalue += split; - range = range - split; - } - else - { - range = split; - } - - range <<= 1; - - if ((lowvalue & 0x80000000)) - { - int x = w->pos - 1; - - while (x >= 0 && w->buffer[x] == 0xff) - { - w->buffer[x] = (unsigned char)0; - x--; - } - - w->buffer[x] += 1; - - } - - lowvalue <<= 1; - - if (!++count) - { - count = -8; - validate_buffer(w->buffer + w->pos, - 1, - cx_data_end, - &cpi->common.error); - - w->buffer[w->pos++] = (lowvalue >> 24); - - lowvalue &= 0xffffff; - } - } - - } - - ++p; - } - } - - w->count = count; - w->lowvalue = lowvalue; - w->range = range; - + pack_tokens_c(w, p, tokens); } vp8_stop_encode(w); @@ -605,209 +409,17 @@ static void pack_mb_row_tokens_c(VP8_COMP *cpi, vp8_writer *w) { - - unsigned int split; - int count = w->count; - unsigned int range = w->range; - unsigned int lowvalue = w->lowvalue; - unsigned int shift; int mb_row; for (mb_row = 0; mb_row < cpi->common.mb_rows; mb_row++) { - TOKENEXTRA *p = cpi->tplist[mb_row].start; - TOKENEXTRA *stop = cpi->tplist[mb_row].stop; + const TOKENEXTRA *p = cpi->tplist[mb_row].start; + const TOKENEXTRA *stop = cpi->tplist[mb_row].stop; + int tokens = stop - p; - while (p < stop) - { - const int t = p->Token; - vp8_token *const a = vp8_coef_encodings + t; - const vp8_extra_bit_struct *const b = vp8_extra_bits + t; - int i = 0; - const unsigned char *pp = p->context_tree; - int v = a->value; - int n = a->Len; - - if (p->skip_eob_node) - { - n--; - i = 2; - } - - do - { - const int bb = (v >> --n) & 1; - split = 1 + (((range - 1) * pp[i>>1]) >> 8); - i = vp8_coef_tree[i+bb]; - - if (bb) - { - lowvalue += split; - range = range - split; - } - else - { - range = split; - } - - shift = vp8_norm[range]; - range <<= shift; - count += shift; - - if (count >= 0) - { - int offset = shift - count; - - if ((lowvalue << (offset - 1)) & 0x80000000) - { - int x = w->pos - 1; - - while (x >= 0 && w->buffer[x] == 0xff) - { - w->buffer[x] = (unsigned char)0; - x--; - } - - w->buffer[x] += 1; - } - - validate_buffer(w->buffer + w->pos, - 1, - w->buffer_end, - w->error); - - w->buffer[w->pos++] = (lowvalue >> (24 - offset)); - lowvalue <<= offset; - shift = count; - lowvalue &= 0xffffff; - count -= 8 ; - } - - lowvalue <<= shift; - } - while (n); - - - if (b->base_val) - { - const int e = p->Extra, L = b->Len; - - if (L) - { - const unsigned char *pp = b->prob; - int v = e >> 1; - int n = L; /* number of bits in v, assumed nonzero */ - int i = 0; - - do - { - const int bb = (v >> --n) & 1; - split = 1 + (((range - 1) * pp[i>>1]) >> 8); - i = b->tree[i+bb]; - - if (bb) - { - lowvalue += split; - range = range - split; - } - else - { - range = split; - } - - shift = vp8_norm[range]; - range <<= shift; - count += shift; - - if (count >= 0) - { - int offset = shift - count; - - if ((lowvalue << (offset - 1)) & 0x80000000) - { - int x = w->pos - 1; - - while (x >= 0 && w->buffer[x] == 0xff) - { - w->buffer[x] = (unsigned char)0; - x--; - } - - w->buffer[x] += 1; - } - - validate_buffer(w->buffer + w->pos, - 1, - w->buffer_end, - w->error); - - w->buffer[w->pos++] = (lowvalue >> (24 - offset)); - lowvalue <<= offset; - shift = count; - lowvalue &= 0xffffff; - count -= 8 ; - } - - lowvalue <<= shift; - } - while (n); - } - - { - split = (range + 1) >> 1; - - if (e & 1) - { - lowvalue += split; - range = range - split; - } - else - { - range = split; - } - - range <<= 1; - - if ((lowvalue & 0x80000000)) - { - int x = w->pos - 1; - - while (x >= 0 && w->buffer[x] == 0xff) - { - w->buffer[x] = (unsigned char)0; - x--; - } - - w->buffer[x] += 1; - - } - - lowvalue <<= 1; - - if (!++count) - { - count = -8; - - validate_buffer(w->buffer + w->pos, - 1, - w->buffer_end, - w->error); - - w->buffer[w->pos++] = (lowvalue >> 24); - lowvalue &= 0xffffff; - } - } - - } - - ++p; - } + pack_tokens_c(w, p, tokens); } - w->count = count; - w->lowvalue = lowvalue; - w->range = range; - } static void write_mv_ref @@ -925,7 +537,9 @@ if (pc->mb_no_coeff_skip) { - prob_skip_false = cpi->skip_false_count * 256 / (cpi->skip_false_count + cpi->skip_true_count); + int total_mbs = pc->mb_rows * pc->mb_cols; + + prob_skip_false = (total_mbs - cpi->skip_true_count ) * 256 / total_mbs; if (prob_skip_false <= 1) prob_skip_false = 1; @@ -1112,7 +726,9 @@ if (c->mb_no_coeff_skip) { - prob_skip_false = cpi->skip_false_count * 256 / (cpi->skip_false_count + cpi->skip_true_count); + int total_mbs = c->mb_rows * c->mb_cols; + + prob_skip_false = (total_mbs - cpi->skip_true_count ) * 256 / total_mbs; if (prob_skip_false <= 1) prob_skip_false = 1;
diff --git a/vp8/encoder/encodeframe.c b/vp8/encoder/encodeframe.c index 10f5607..9899565 100644 --- a/vp8/encoder/encodeframe.c +++ b/vp8/encoder/encodeframe.c
@@ -688,15 +688,12 @@ xd->subpixel_predict16x16 = vp8_bilinear_predict16x16; } - // Reset frame count of inter 0,0 motion vector useage. + // Reset frame count of inter 0,0 motion vector usage. cpi->inter_zz_count = 0; - vpx_memset(segment_counts, 0, sizeof(segment_counts)); - cpi->prediction_error = 0; cpi->intra_error = 0; cpi->skip_true_count = 0; - cpi->skip_false_count = 0; #if 0 // Experimental code @@ -1094,6 +1091,7 @@ vp8_encode_intra16x16mbuv(x); sum_intra_stats(cpi, x); + vp8_tokenize_mb(cpi, &x->e_mbd, t); if (xd->mode_info_context->mbmi.mode != B_PRED) @@ -1260,11 +1258,6 @@ if (!x->skip) { vp8_encode_inter16x16(x); - - // Clear mb_skip_coeff if mb_no_coeff_skip is not set - if (!cpi->common.mb_no_coeff_skip) - xd->mode_info_context->mbmi.mb_skip_coeff = 0; - } else vp8_build_inter16x16_predictors_mb(xd, xd->dst.y_buffer, @@ -1287,17 +1280,17 @@ } else { + /* always set mb_skip_coeff as it is needed by the loopfilter */ + xd->mode_info_context->mbmi.mb_skip_coeff = 1; + if (cpi->common.mb_no_coeff_skip) { - xd->mode_info_context->mbmi.mb_skip_coeff = 1; cpi->skip_true_count ++; vp8_fix_contexts(xd); } else { vp8_stuff_mb(cpi, xd, t); - xd->mode_info_context->mbmi.mb_skip_coeff = 0; - cpi->skip_false_count ++; } }
diff --git a/vp8/encoder/ethreading.c b/vp8/encoder/ethreading.c index 7740e5d..2874e78 100644 --- a/vp8/encoder/ethreading.c +++ b/vp8/encoder/ethreading.c
@@ -24,9 +24,9 @@ extern void vp8_build_block_offsets(MACROBLOCK *x); extern void vp8_setup_block_ptrs(MACROBLOCK *x); -extern void loopfilter_frame(VP8_COMP *cpi, VP8_COMMON *cm); +extern void vp8_loopfilter_frame(VP8_COMP *cpi, VP8_COMMON *cm); -static THREAD_FUNCTION loopfilter_thread(void *p_data) +static THREAD_FUNCTION thread_loopfilter(void *p_data) { VP8_COMP *cpi = (VP8_COMP *)(((LPFTHREAD_DATA *)p_data)->ptr1); VP8_COMMON *cm = &cpi->common; @@ -41,7 +41,7 @@ if (cpi->b_multi_threaded == 0) // we're shutting down break; - loopfilter_frame(cpi, cm); + vp8_loopfilter_frame(cpi, cm); sem_post(&cpi->h_event_end_lpf); } @@ -468,6 +468,7 @@ cpi->b_multi_threaded = 0; cpi->encoding_thread_count = 0; + cpi->b_lpf_running = 0; if (cm->processor_core_count > 1 && cpi->oxcf.multi_threaded > 1) { @@ -526,7 +527,7 @@ sem_init(&cpi->h_event_end_lpf, 0, 0); lpfthd->ptr1 = (void *)cpi; - pthread_create(&cpi->h_filter_thread, 0, loopfilter_thread, lpfthd); + pthread_create(&cpi->h_filter_thread, 0, thread_loopfilter, lpfthd); } }
diff --git a/vp8/encoder/onyx_if.c b/vp8/encoder/onyx_if.c index 57656bb..6645442 100644 --- a/vp8/encoder/onyx_if.c +++ b/vp8/encoder/onyx_if.c
@@ -3086,7 +3086,7 @@ } } -void loopfilter_frame(VP8_COMP *cpi, VP8_COMMON *cm) +void vp8_loopfilter_frame(VP8_COMP *cpi, VP8_COMMON *cm) { if (cm->no_lpf) { @@ -3716,6 +3716,15 @@ vp8_setup_key_frame(cpi); } +#if CONFIG_MULTITHREAD + /* wait for the last picture loopfilter thread done */ + if (cpi->b_lpf_running) + { + sem_wait(&cpi->h_event_end_lpf); + cpi->b_lpf_running = 0; + } +#endif + // transform / motion compensation build reconstruction frame vp8_encode_frame(cpi); @@ -4074,11 +4083,12 @@ if (cpi->b_multi_threaded) { sem_post(&cpi->h_event_start_lpf); /* start loopfilter in separate thread */ + cpi->b_lpf_running = 1; } else #endif { - loopfilter_frame(cpi, cm); + vp8_loopfilter_frame(cpi, cm); } update_reference_frames(cm); @@ -4098,10 +4108,11 @@ vp8_pack_bitstream(cpi, dest, dest_end, size); #if CONFIG_MULTITHREAD - /* wait for loopfilter thread done */ - if (cpi->b_multi_threaded) + /* if PSNR packets are generated we have to wait for the lpf */ + if (cpi->b_lpf_running && cpi->b_calculate_psnr) { sem_wait(&cpi->h_event_end_lpf); + cpi->b_lpf_running = 0; } #endif @@ -4564,7 +4575,7 @@ vpx_usec_timer_start(&timer); /* Reinit the lookahead buffer if the frame size changes */ - if (sd->y_width != cpi->common.Width || sd->y_height != cpi->common.Height) + if (sd->y_width != cpi->oxcf.Width || sd->y_height != cpi->oxcf.Height) { assert(cpi->oxcf.lag_in_frames < 2); dealloc_raw_frame_buffers(cpi); @@ -5107,6 +5118,15 @@ else { int ret; + +#if CONFIG_MULTITHREAD + if(cpi->b_lpf_running) + { + sem_wait(&cpi->h_event_end_lpf); + cpi->b_lpf_running = 0; + } +#endif + #if CONFIG_POSTPROC ret = vp8_post_proc_frame(&cpi->common, dest, flags); #else
diff --git a/vp8/encoder/onyx_int.h b/vp8/encoder/onyx_int.h index 6920fc3..dcefbac 100644 --- a/vp8/encoder/onyx_int.h +++ b/vp8/encoder/onyx_int.h
@@ -504,7 +504,6 @@ int gf_bad_count; int gf_update_recommended; int skip_true_count; - int skip_false_count; unsigned char *segmentation_map; signed char segment_feature_data[MB_LVL_MAX][MAX_MB_SEGMENTS]; // Segment data (can be deltas or absolute values) @@ -526,6 +525,7 @@ int mt_sync_range; int b_multi_threaded; int encoding_thread_count; + int b_lpf_running; pthread_t *h_encoding_thread; pthread_t h_filter_thread;
diff --git a/vp8/encoder/ratectrl.c b/vp8/encoder/ratectrl.c index 1c43c11..f4ed300 100644 --- a/vp8/encoder/ratectrl.c +++ b/vp8/encoder/ratectrl.c
@@ -392,10 +392,16 @@ int Q = (cpi->common.frame_flags & FRAMEFLAGS_KEY) ? cpi->avg_frame_qindex : cpi->ni_av_qi; - // Boost depends somewhat on frame rate - kf_boost = (int)(2 * cpi->output_frame_rate - 16); + // Boost depends somewhat on frame rate: only used for 1 layer case. + if (cpi->oxcf.number_of_layers == 1) { + kf_boost = (int)(2 * cpi->output_frame_rate - 16); + } + else { + // Initial factor: set target size to: |2.5 * per_frame_bandwidth|. + kf_boost = 24; + } - // adjustment up based on q + // adjustment up based on q: this factor ranges from ~1.2 to 2.2. kf_boost = kf_boost * kf_boost_qadjustment[Q] / 100; // frame separation adjustment ( down) @@ -403,6 +409,7 @@ kf_boost = (int)(kf_boost * cpi->frames_since_key / (cpi->output_frame_rate / 2)); + // Minimal target size is |2* per_frame_bandwidth|. if (kf_boost < 16) kf_boost = 16;
diff --git a/vp8/encoder/rdopt.c b/vp8/encoder/rdopt.c index 14000f9..80e0f4e 100644 --- a/vp8/encoder/rdopt.c +++ b/vp8/encoder/rdopt.c
@@ -1738,11 +1738,12 @@ int rate2, distortion2; int uv_intra_rate, uv_intra_distortion, uv_intra_rate_tokenonly; int uv_intra_tteob = 0; + int uv_intra_done = 0; int rate_y, UNINITIALIZED_IS_SAFE(rate_uv); int distortion_uv; int best_yrd = INT_MAX; - MB_PREDICTION_MODE uv_intra_mode; + MB_PREDICTION_MODE uv_intra_mode = 0; int_mv mvp; int near_sadidx[8] = {0, 1, 2, 3, 4, 5, 6, 7}; int saddone=0; @@ -1785,17 +1786,6 @@ x->skip = 0; - x->e_mbd.mode_info_context->mbmi.ref_frame = INTRA_FRAME; - rd_pick_intra_mbuv_mode(cpi, x, &uv_intra_rate, &uv_intra_rate_tokenonly, &uv_intra_distortion); - uv_intra_mode = x->e_mbd.mode_info_context->mbmi.uv_mode; - /* - * Total of the eobs is used later to further adjust rate2. Since uv block's - * intra eobs will be overwritten when we check inter modes in following - * for-loop, we need to save uv_intra_tteob here. - */ - for (i = 16; i < 24; i++) - uv_intra_tteob += x->e_mbd.eobs[i]; - for (mode_index = 0; mode_index < MAX_MODES; mode_index++) { int this_rd = INT_MAX; @@ -1817,7 +1807,6 @@ this_mode = vp8_mode_order[mode_index]; x->e_mbd.mode_info_context->mbmi.mode = this_mode; - x->e_mbd.mode_info_context->mbmi.uv_mode = DC_PRED; x->e_mbd.mode_info_context->mbmi.ref_frame = this_ref_frame; // Only consider ZEROMV/ALTREF_FRAME for alt ref frame, @@ -1888,6 +1877,24 @@ vp8_update_zbin_extra(cpi, x); } + if(!uv_intra_done && this_ref_frame == INTRA_FRAME) + { + rd_pick_intra_mbuv_mode(cpi, x, &uv_intra_rate, + &uv_intra_rate_tokenonly, + &uv_intra_distortion); + uv_intra_mode = x->e_mbd.mode_info_context->mbmi.uv_mode; + + /* + * Total of the eobs is used later to further adjust rate2. Since uv + * block's intra eobs will be overwritten when we check inter modes, + * we need to save uv_intra_tteob here. + */ + for (i = 16; i < 24; i++) + uv_intra_tteob += x->e_mbd.eobs[i]; + + uv_intra_done = 1; + } + switch (this_mode) { case B_PRED: @@ -2214,22 +2221,22 @@ if (cpi->common.mb_no_coeff_skip) { int tteob; + int has_y2_block = (this_mode!=SPLITMV && this_mode!=B_PRED); tteob = 0; + if(has_y2_block) + tteob += x->e_mbd.eobs[24]; + + for (i = 0; i < 16; i++) + tteob += (x->e_mbd.eobs[i] > has_y2_block); if (x->e_mbd.mode_info_context->mbmi.ref_frame) { - for (i = 0; i <= 24; i++) + for (i = 16; i < 24; i++) tteob += x->e_mbd.eobs[i]; } else - { - for (i = 0; i < 16; i++) - tteob += x->e_mbd.eobs[i]; - tteob += uv_intra_tteob; - tteob += x->e_mbd.eobs[24]; - } if (tteob == 0) {
diff --git a/vp8/encoder/tokenize.c b/vp8/encoder/tokenize.c index 8bfc47f..967b602 100644 --- a/vp8/encoder/tokenize.c +++ b/vp8/encoder/tokenize.c
@@ -378,30 +378,27 @@ x->mode_info_context->mbmi.mb_skip_coeff = mb_is_skippable(x, has_y2_block); if (x->mode_info_context->mbmi.mb_skip_coeff) { - cpi->skip_true_count++; - if (!cpi->common.mb_no_coeff_skip) - vp8_stuff_mb(cpi, x, t) ; + { + vp8_stuff_mb(cpi, x, t); + } else { vp8_fix_contexts(x); + cpi->skip_true_count++; } return; } - cpi->skip_false_count++; - plane_type = 3; if(has_y2_block) { tokenize2nd_order_b(x, t, cpi); plane_type = 0; - } tokenize1st_order_b(x, t, plane_type, cpi); - }
diff --git a/vp8/vp8_common.mk b/vp8/vp8_common.mk index b2db4ce..a200b22 100644 --- a/vp8/vp8_common.mk +++ b/vp8/vp8_common.mk
@@ -30,6 +30,7 @@ VP8_COMMON_SRCS-yes += common/generic/systemdependent.c VP8_COMMON_SRCS-yes += common/idct_blk.c VP8_COMMON_SRCS-yes += common/idctllm.c +VP8_COMMON_SRCS-yes += common/idctllm_test.cc VP8_COMMON_SRCS-yes += common/alloccommon.h VP8_COMMON_SRCS-yes += common/blockd.h VP8_COMMON_SRCS-yes += common/common.h @@ -80,6 +81,7 @@ VP8_COMMON_SRCS-$(HAVE_MMX) += common/x86/dequantize_mmx.asm VP8_COMMON_SRCS-$(HAVE_MMX) += common/x86/idct_blk_mmx.c VP8_COMMON_SRCS-$(HAVE_MMX) += common/x86/idctllm_mmx.asm +VP8_COMMON_SRCS-$(HAVE_MMX) += common/x86/idctllm_mmx_test.cc VP8_COMMON_SRCS-$(HAVE_MMX) += common/x86/iwalsh_mmx.asm VP8_COMMON_SRCS-$(HAVE_MMX) += common/x86/recon_mmx.asm VP8_COMMON_SRCS-$(HAVE_MMX) += common/x86/subpixel_mmx.asm
diff --git a/vpxenc.c b/vpxenc.c index bb0a306..7d6758a 100644 --- a/vpxenc.c +++ b/vpxenc.c
@@ -116,13 +116,17 @@ } -static void ctx_exit_on_error(vpx_codec_ctx_t *ctx, const char *s) +static void ctx_exit_on_error(vpx_codec_ctx_t *ctx, const char *s, ...) { + va_list ap; + + va_start(ap, s); if (ctx->err) { const char *detail = vpx_codec_error_detail(ctx); - fprintf(stderr, "%s: %s\n", s, vpx_codec_error(ctx)); + vfprintf(stderr, s, ap); + fprintf(stderr, ": %s\n", vpx_codec_error(ctx)); if (detail) fprintf(stderr, " %s\n", detail); @@ -1480,8 +1484,6 @@ const struct codec_item *codec; int passes; int pass; - const char *stats_fn; - const char *out_fn; int usage; int deadline; int use_i420; @@ -1490,13 +1492,47 @@ int show_psnr; int have_framerate; struct vpx_rational framerate; - int write_webm; int debug; int show_q_hist_buckets; int show_rate_hist_buckets; }; +/* Per-stream configuration */ +struct stream_config +{ + struct vpx_codec_enc_cfg cfg; + const char *out_fn; + const char *stats_fn; + stereo_format_t stereo_fmt; + int arg_ctrls[ARG_CTRL_CNT_MAX][2]; + int arg_ctrl_cnt; + int write_webm; +}; + + +struct stream_state +{ + int index; + struct stream_state *next; + struct stream_config config; + FILE *file; + struct rate_hist rate_hist; + EbmlGlobal ebml; + uint32_t hash; + uint64_t psnr_sse_total; + uint64_t psnr_samples_total; + double psnr_totals[4]; + int psnr_count; + int counts[64]; + vpx_codec_ctx_t encoder; + unsigned int frames_out; + uint64_t cx_time; + size_t nbytes; + stats_io_t stats; +}; + + static void parse_global_config(struct global_config *global, char **argv) { char **argi, **argj; @@ -1507,7 +1543,6 @@ global->codec = codecs; global->passes = 1; global->use_i420 = 1; - global->write_webm = 1; for (argi = argj = argv; (*argj = *argi); argi += arg.argv_step) { @@ -1543,8 +1578,6 @@ die("Error: Invalid pass selected (%d)\n", global->pass); } - else if (arg_match(&arg, &fpf_name, argi)) - global->stats_fn = arg.val; else if (arg_match(&arg, &usage, argi)) global->usage = arg_parse_uint(&arg); else if (arg_match(&arg, &deadline, argi)) @@ -1570,10 +1603,6 @@ global->framerate = arg_parse_rational(&arg); global->have_framerate = 1; } - else if (arg_match(&arg, &use_ivf, argi)) - global->write_webm = 0; - else if (arg_match(&arg, &outputfile, argi)) - global->out_fn = arg.val; else if (arg_match(&arg, &debugmode, argi)) global->debug = 1; else if (arg_match(&arg, &q_hist_n, argi)) @@ -1586,9 +1615,6 @@ /* Validate global config */ - /* Ensure that --passes and --pass are consistent. If --pass is set and - * --passes=2, ensure --fpf was set. - */ if (global->pass) { /* DWIM: Assume the user meant passes=2 if pass=2 is specified */ @@ -1598,10 +1624,6 @@ global->pass, global->pass); global->passes = global->pass; } - - if (global->passes == 2 && !global->stats_fn) - die("Must specify --fpf when --pass=%d and --passes=2\n", - global->pass); } } @@ -1667,44 +1689,578 @@ y4m_input_close(&input->y4m); } +static struct stream_state *new_stream(struct global_config *global, + struct stream_state *prev) +{ + struct stream_state *stream; + + stream = calloc(1, sizeof(*stream)); + if(!stream) + fatal("Failed to allocate new stream."); + if(prev) + { + memcpy(stream, prev, sizeof(*stream)); + stream->index++; + prev->next = stream; + } + else + { + vpx_codec_err_t res; + + /* Populate encoder configuration */ + res = vpx_codec_enc_config_default(global->codec->iface, + &stream->config.cfg, + global->usage); + if (res) + fatal("Failed to get config: %s\n", vpx_codec_err_to_string(res)); + + /* Change the default timebase to a high enough value so that the + * encoder will always create strictly increasing timestamps. + */ + stream->config.cfg.g_timebase.den = 1000; + + /* Never use the library's default resolution, require it be parsed + * from the file or set on the command line. + */ + stream->config.cfg.g_w = 0; + stream->config.cfg.g_h = 0; + + /* Initialize remaining stream parameters */ + stream->config.stereo_fmt = STEREO_FORMAT_MONO; + stream->config.write_webm = 1; + stream->ebml.last_pts_ms = -1; + } + + /* Output files must be specified for each stream */ + stream->config.out_fn = NULL; + + stream->next = NULL; + return stream; +} + + +static int parse_stream_params(struct global_config *global, + struct stream_state *stream, + char **argv) +{ + char **argi, **argj; + struct arg arg; + static const arg_def_t **ctrl_args = no_args; + static const int *ctrl_args_map = NULL; + struct stream_config *config = &stream->config; + int eos_mark_found = 0; + + /* Handle codec specific options */ + if (global->codec->iface == &vpx_codec_vp8_cx_algo) + { + ctrl_args = vp8_args; + ctrl_args_map = vp8_arg_ctrl_map; + } + + for (argi = argj = argv; (*argj = *argi); argi += arg.argv_step) + { + arg.argv_step = 1; + + /* Once we've found an end-of-stream marker (--) we want to continue + * shifting arguments but not consuming them. + */ + if (eos_mark_found) + { + argj++; + continue; + } + else if (!strcmp(*argj, "--")) + { + eos_mark_found = 1; + continue; + } + + if (0); + else if (arg_match(&arg, &outputfile, argi)) + config->out_fn = arg.val; + else if (arg_match(&arg, &fpf_name, argi)) + config->stats_fn = arg.val; + else if (arg_match(&arg, &use_ivf, argi)) + config->write_webm = 0; + else if (arg_match(&arg, &threads, argi)) + config->cfg.g_threads = arg_parse_uint(&arg); + else if (arg_match(&arg, &profile, argi)) + config->cfg.g_profile = arg_parse_uint(&arg); + else if (arg_match(&arg, &width, argi)) + config->cfg.g_w = arg_parse_uint(&arg); + else if (arg_match(&arg, &height, argi)) + config->cfg.g_h = arg_parse_uint(&arg); + else if (arg_match(&arg, &stereo_mode, argi)) + config->stereo_fmt = arg_parse_enum_or_int(&arg); + else if (arg_match(&arg, &timebase, argi)) + config->cfg.g_timebase = arg_parse_rational(&arg); + else if (arg_match(&arg, &error_resilient, argi)) + config->cfg.g_error_resilient = arg_parse_uint(&arg); + else if (arg_match(&arg, &lag_in_frames, argi)) + config->cfg.g_lag_in_frames = arg_parse_uint(&arg); + else if (arg_match(&arg, &dropframe_thresh, argi)) + config->cfg.rc_dropframe_thresh = arg_parse_uint(&arg); + else if (arg_match(&arg, &resize_allowed, argi)) + config->cfg.rc_resize_allowed = arg_parse_uint(&arg); + else if (arg_match(&arg, &resize_up_thresh, argi)) + config->cfg.rc_resize_up_thresh = arg_parse_uint(&arg); + else if (arg_match(&arg, &resize_down_thresh, argi)) + config->cfg.rc_resize_down_thresh = arg_parse_uint(&arg); + else if (arg_match(&arg, &end_usage, argi)) + config->cfg.rc_end_usage = arg_parse_enum_or_int(&arg); + else if (arg_match(&arg, &target_bitrate, argi)) + config->cfg.rc_target_bitrate = arg_parse_uint(&arg); + else if (arg_match(&arg, &min_quantizer, argi)) + config->cfg.rc_min_quantizer = arg_parse_uint(&arg); + else if (arg_match(&arg, &max_quantizer, argi)) + config->cfg.rc_max_quantizer = arg_parse_uint(&arg); + else if (arg_match(&arg, &undershoot_pct, argi)) + config->cfg.rc_undershoot_pct = arg_parse_uint(&arg); + else if (arg_match(&arg, &overshoot_pct, argi)) + config->cfg.rc_overshoot_pct = arg_parse_uint(&arg); + else if (arg_match(&arg, &buf_sz, argi)) + config->cfg.rc_buf_sz = arg_parse_uint(&arg); + else if (arg_match(&arg, &buf_initial_sz, argi)) + config->cfg.rc_buf_initial_sz = arg_parse_uint(&arg); + else if (arg_match(&arg, &buf_optimal_sz, argi)) + config->cfg.rc_buf_optimal_sz = arg_parse_uint(&arg); + else if (arg_match(&arg, &bias_pct, argi)) + { + config->cfg.rc_2pass_vbr_bias_pct = arg_parse_uint(&arg); + + if (global->passes < 2) + warn("option %s ignored in one-pass mode.\n", arg.name); + } + else if (arg_match(&arg, &minsection_pct, argi)) + { + config->cfg.rc_2pass_vbr_minsection_pct = arg_parse_uint(&arg); + + if (global->passes < 2) + warn("option %s ignored in one-pass mode.\n", arg.name); + } + else if (arg_match(&arg, &maxsection_pct, argi)) + { + config->cfg.rc_2pass_vbr_maxsection_pct = arg_parse_uint(&arg); + + if (global->passes < 2) + warn("option %s ignored in one-pass mode.\n", arg.name); + } + else if (arg_match(&arg, &kf_min_dist, argi)) + config->cfg.kf_min_dist = arg_parse_uint(&arg); + else if (arg_match(&arg, &kf_max_dist, argi)) + config->cfg.kf_max_dist = arg_parse_uint(&arg); + else if (arg_match(&arg, &kf_disabled, argi)) + config->cfg.kf_mode = VPX_KF_DISABLED; + else + { + int i, match = 0; + + for (i = 0; ctrl_args[i]; i++) + { + if (arg_match(&arg, ctrl_args[i], argi)) + { + int j; + match = 1; + + /* Point either to the next free element or the first + * instance of this control. + */ + for(j=0; j<config->arg_ctrl_cnt; j++) + if(config->arg_ctrls[j][0] == ctrl_args_map[i]) + break; + + /* Update/insert */ + assert(j < ARG_CTRL_CNT_MAX); + if (j < ARG_CTRL_CNT_MAX) + { + config->arg_ctrls[j][0] = ctrl_args_map[i]; + config->arg_ctrls[j][1] = arg_parse_enum_or_int(&arg); + if(j == config->arg_ctrl_cnt) + config->arg_ctrl_cnt++; + } + + } + } + + if (!match) + argj++; + } + } + + return eos_mark_found; +} + + +#define FOREACH_STREAM(func)\ +do\ +{\ + struct stream_state *stream;\ +\ + for(stream = streams; stream; stream = stream->next)\ + func;\ +}while(0) + + +static void validate_stream_config(struct stream_state *stream) +{ + struct stream_state *streami; + + if(!stream->config.cfg.g_w || !stream->config.cfg.g_h) + fatal("Stream %d: Specify stream dimensions with --width (-w) " + " and --height (-h)", stream->index); + + for(streami = stream; streami; streami = streami->next) + { + /* All streams require output files */ + if(!streami->config.out_fn) + fatal("Stream %d: Output file is required (specify with -o)", + streami->index); + + /* Check for two streams outputting to the same file */ + if(streami != stream) + { + const char *a = stream->config.out_fn; + const char *b = streami->config.out_fn; + if(!strcmp(a,b) && strcmp(a, "/dev/null") && strcmp(a, ":nul")) + fatal("Stream %d: duplicate output file (from stream %d)", + streami->index, stream->index); + } + + /* Check for two streams sharing a stats file. */ + if(streami != stream) + { + const char *a = stream->config.stats_fn; + const char *b = streami->config.stats_fn; + if(a && b && !strcmp(a,b)) + fatal("Stream %d: duplicate stats file (from stream %d)", + streami->index, stream->index); + } + } +} + + +static void set_stream_dimensions(struct stream_state *stream, + unsigned int w, + unsigned int h) +{ + if ((stream->config.cfg.g_w && stream->config.cfg.g_w != w) + ||(stream->config.cfg.g_h && stream->config.cfg.g_h != h)) + fatal("Stream %d: Resizing not yet supported", stream->index); + stream->config.cfg.g_w = w; + stream->config.cfg.g_h = h; +} + + +static void show_stream_config(struct stream_state *stream, + struct global_config *global, + struct input_state *input) +{ + +#define SHOW(field) \ + fprintf(stderr, " %-28s = %d\n", #field, stream->config.cfg.field) + + if(stream->index == 0) + { + fprintf(stderr, "Codec: %s\n", + vpx_codec_iface_name(global->codec->iface)); + fprintf(stderr, "Source file: %s Format: %s\n", input->fn, + input->use_i420 ? "I420" : "YV12"); + } + if(stream->next || stream->index) + fprintf(stderr, "\nStream Index: %d\n", stream->index); + fprintf(stderr, "Destination file: %s\n", stream->config.out_fn); + fprintf(stderr, "Encoder parameters:\n"); + + SHOW(g_usage); + SHOW(g_threads); + SHOW(g_profile); + SHOW(g_w); + SHOW(g_h); + SHOW(g_timebase.num); + SHOW(g_timebase.den); + SHOW(g_error_resilient); + SHOW(g_pass); + SHOW(g_lag_in_frames); + SHOW(rc_dropframe_thresh); + SHOW(rc_resize_allowed); + SHOW(rc_resize_up_thresh); + SHOW(rc_resize_down_thresh); + SHOW(rc_end_usage); + SHOW(rc_target_bitrate); + SHOW(rc_min_quantizer); + SHOW(rc_max_quantizer); + SHOW(rc_undershoot_pct); + SHOW(rc_overshoot_pct); + SHOW(rc_buf_sz); + SHOW(rc_buf_initial_sz); + SHOW(rc_buf_optimal_sz); + SHOW(rc_2pass_vbr_bias_pct); + SHOW(rc_2pass_vbr_minsection_pct); + SHOW(rc_2pass_vbr_maxsection_pct); + SHOW(kf_mode); + SHOW(kf_min_dist); + SHOW(kf_max_dist); +} + + +static void open_output_file(struct stream_state *stream, + struct global_config *global) +{ + const char *fn = stream->config.out_fn; + + stream->file = strcmp(fn, "-") ? fopen(fn, "wb") : set_binary_mode(stdout); + + if (!stream->file) + fatal("Failed to open output file"); + + if(stream->config.write_webm && fseek(stream->file, 0, SEEK_CUR)) + fatal("WebM output to pipes not supported."); + + if(stream->config.write_webm) + { + stream->ebml.stream = stream->file; + write_webm_file_header(&stream->ebml, &stream->config.cfg, + &global->framerate, + stream->config.stereo_fmt); + } + else + write_ivf_file_header(stream->file, &stream->config.cfg, + global->codec->fourcc, 0); +} + + +static void close_output_file(struct stream_state *stream, + unsigned int fourcc) +{ + if(stream->config.write_webm) + { + write_webm_file_footer(&stream->ebml, stream->hash); + free(stream->ebml.cue_list); + stream->ebml.cue_list = NULL; + } + else + { + if (!fseek(stream->file, 0, SEEK_SET)) + write_ivf_file_header(stream->file, &stream->config.cfg, + fourcc, + stream->frames_out); + } + + fclose(stream->file); +} + + +static void setup_pass(struct stream_state *stream, + struct global_config *global, + int pass) +{ + if (stream->config.stats_fn) + { + if (!stats_open_file(&stream->stats, stream->config.stats_fn, + pass)) + fatal("Failed to open statistics store"); + } + else + { + if (!stats_open_mem(&stream->stats, pass)) + fatal("Failed to open statistics store"); + } + + stream->config.cfg.g_pass = global->passes == 2 + ? pass ? VPX_RC_LAST_PASS : VPX_RC_FIRST_PASS + : VPX_RC_ONE_PASS; + if (pass) + stream->config.cfg.rc_twopass_stats_in = stats_get(&stream->stats); +} + + +static void initialize_encoder(struct stream_state *stream, + struct global_config *global) +{ + int i; + + /* Construct Encoder Context */ + vpx_codec_enc_init(&stream->encoder, global->codec->iface, + &stream->config.cfg, + global->show_psnr ? VPX_CODEC_USE_PSNR : 0); + ctx_exit_on_error(&stream->encoder, "Failed to initialize encoder"); + + /* Note that we bypass the vpx_codec_control wrapper macro because + * we're being clever to store the control IDs in an array. Real + * applications will want to make use of the enumerations directly + */ + for (i = 0; i < stream->config.arg_ctrl_cnt; i++) + { + int ctrl = stream->config.arg_ctrls[i][0]; + int value = stream->config.arg_ctrls[i][1]; + if (vpx_codec_control_(&stream->encoder, ctrl, value)) + fprintf(stderr, "Error: Tried to set control %d = %d\n", + ctrl, value); + + ctx_exit_on_error(&stream->encoder, "Failed to control codec"); + } +} + + +static void encode_frame(struct stream_state *stream, + struct global_config *global, + struct vpx_image *img, + unsigned int frames_in) +{ + vpx_codec_pts_t frame_start, next_frame_start; + struct vpx_codec_enc_cfg *cfg = &stream->config.cfg; + struct vpx_usec_timer timer; + + frame_start = (cfg->g_timebase.den * (int64_t)(frames_in - 1) + * global->framerate.den) + / cfg->g_timebase.num / global->framerate.num; + next_frame_start = (cfg->g_timebase.den * (int64_t)(frames_in) + * global->framerate.den) + / cfg->g_timebase.num / global->framerate.num; + vpx_usec_timer_start(&timer); + vpx_codec_encode(&stream->encoder, img, frame_start, + next_frame_start - frame_start, + 0, global->deadline); + vpx_usec_timer_mark(&timer); + stream->cx_time += vpx_usec_timer_elapsed(&timer); + ctx_exit_on_error(&stream->encoder, "Stream %d: Failed to encode frame", + stream->index); +} + + +static void update_quantizer_histogram(struct stream_state *stream) +{ + if(stream->config.cfg.g_pass != VPX_RC_FIRST_PASS) + { + int q; + + vpx_codec_control(&stream->encoder, VP8E_GET_LAST_QUANTIZER_64, &q); + ctx_exit_on_error(&stream->encoder, "Failed to read quantizer"); + stream->counts[q]++; + } +} + + +static void get_cx_data(struct stream_state *stream, + struct global_config *global, + int *got_data) +{ + const vpx_codec_cx_pkt_t *pkt; + const struct vpx_codec_enc_cfg *cfg = &stream->config.cfg; + vpx_codec_iter_t iter = NULL; + + while ((pkt = vpx_codec_get_cx_data(&stream->encoder, &iter))) + { + *got_data = 1; + + switch (pkt->kind) + { + case VPX_CODEC_CX_FRAME_PKT: + stream->frames_out++; + fprintf(stderr, " %6luF", + (unsigned long)pkt->data.frame.sz); + + update_rate_histogram(&stream->rate_hist, cfg, pkt); + if(stream->config.write_webm) + { + /* Update the hash */ + if(!stream->ebml.debug) + stream->hash = murmur(pkt->data.frame.buf, + pkt->data.frame.sz, stream->hash); + + write_webm_block(&stream->ebml, cfg, pkt); + } + else + { + write_ivf_frame_header(stream->file, pkt); + if(fwrite(pkt->data.frame.buf, 1, + pkt->data.frame.sz, stream->file)); + } + stream->nbytes += pkt->data.raw.sz; + break; + case VPX_CODEC_STATS_PKT: + stream->frames_out++; + fprintf(stderr, " %6luS", + (unsigned long)pkt->data.twopass_stats.sz); + stats_write(&stream->stats, + pkt->data.twopass_stats.buf, + pkt->data.twopass_stats.sz); + stream->nbytes += pkt->data.raw.sz; + break; + case VPX_CODEC_PSNR_PKT: + + if (global->show_psnr) + { + int i; + + stream->psnr_sse_total += pkt->data.psnr.sse[0]; + stream->psnr_samples_total += pkt->data.psnr.samples[0]; + for (i = 0; i < 4; i++) + { + fprintf(stderr, "%.3lf ", pkt->data.psnr.psnr[i]); + stream->psnr_totals[i] += pkt->data.psnr.psnr[i]; + } + stream->psnr_count++; + } + + break; + default: + break; + } + } +} + + +static void show_psnr(struct stream_state *stream) +{ + int i; + double ovpsnr; + + if (!stream->psnr_count) + return; + + fprintf(stderr, "Stream %d PSNR (Overall/Avg/Y/U/V)", stream->index); + ovpsnr = vp8_mse2psnr(stream->psnr_samples_total, 255.0, + stream->psnr_sse_total); + fprintf(stderr, " %.3lf", ovpsnr); + + for (i = 0; i < 4; i++) + { + fprintf(stderr, " %.3lf", stream->psnr_totals[i]/stream->psnr_count); + } + fprintf(stderr, "\n"); +} + + +float usec_to_fps(uint64_t usec, unsigned int frames) +{ + return usec > 0 ? (float)frames * 1000000.0 / (float)usec : 0; +} + int main(int argc, const char **argv_) { - vpx_codec_ctx_t encoder; - int i; - FILE *outfile; - vpx_codec_enc_cfg_t cfg; - vpx_codec_err_t res; int pass; - stats_io_t stats; vpx_image_t raw; int frame_avail, got_data; struct input_state input = {0}; struct global_config global; - struct arg arg; - char **argv, **argi, **argj; - int arg_ctrls[ARG_CTRL_CNT_MAX][2], arg_ctrl_cnt = 0; - static const arg_def_t **ctrl_args = no_args; - static const int *ctrl_args_map = NULL; + struct stream_state *streams = NULL; + char **argv, **argi; unsigned long cx_time = 0; - - EbmlGlobal ebml = {0}; - uint32_t hash = 0; - uint64_t psnr_sse_total = 0; - uint64_t psnr_samples_total = 0; - double psnr_totals[4] = {0, 0, 0, 0}; - int psnr_count = 0; - stereo_format_t stereo_fmt = STEREO_FORMAT_MONO; - int counts[64]={0}; - struct rate_hist rate_hist={0}; + int stream_cnt = 0; exec_name = argv_[0]; - ebml.last_pts_ms = -1; if (argc < 3) usage_exit(); + /* Setup default input stream settings */ + input.framerate.num = 30; + input.framerate.den = 1; + input.use_i420 = 1; + /* First parse the global configuration values, because we want to apply * other parameters on top of the default configuration provided by the * codec. @@ -1712,154 +2268,20 @@ argv = argv_dup(argc - 1, argv_ + 1); parse_global_config(&global, argv); - /* Populate encoder configuration */ - res = vpx_codec_enc_config_default(global.codec->iface, &cfg, - global.usage); - - if (res) - fatal("Failed to get config: %s", vpx_codec_err_to_string(res)); - - /* Change the default timebase to a high enough value so that the encoder - * will always create strictly increasing timestamps. - */ - cfg.g_timebase.den = 1000; - - /* Never use the library's default resolution, require it be parsed - * from the file or set on the command line. - */ - cfg.g_w = 0; - cfg.g_h = 0; - - /* Setup default input stream settings */ - input.framerate.num = 30; - input.framerate.den = 1; - input.use_i420 = 1; - - /* Now parse the remainder of the parameters. */ - for (argi = argj = argv; (*argj = *argi); argi += arg.argv_step) { - arg.argv_step = 1; + /* Now parse each stream's parameters. Using a local scope here + * due to the use of 'stream' as loop variable in FOREACH_STREAM + * loops + */ + struct stream_state *stream = NULL; - if (0); - else if (arg_match(&arg, &threads, argi)) - cfg.g_threads = arg_parse_uint(&arg); - else if (arg_match(&arg, &profile, argi)) - cfg.g_profile = arg_parse_uint(&arg); - else if (arg_match(&arg, &width, argi)) - cfg.g_w = arg_parse_uint(&arg); - else if (arg_match(&arg, &height, argi)) - cfg.g_h = arg_parse_uint(&arg); - else if (arg_match(&arg, &stereo_mode, argi)) - stereo_fmt = arg_parse_enum_or_int(&arg); - else if (arg_match(&arg, &timebase, argi)) - cfg.g_timebase = arg_parse_rational(&arg); - else if (arg_match(&arg, &error_resilient, argi)) - cfg.g_error_resilient = arg_parse_uint(&arg); - else if (arg_match(&arg, &lag_in_frames, argi)) - cfg.g_lag_in_frames = arg_parse_uint(&arg); - else if (arg_match(&arg, &dropframe_thresh, argi)) - cfg.rc_dropframe_thresh = arg_parse_uint(&arg); - else if (arg_match(&arg, &resize_allowed, argi)) - cfg.rc_resize_allowed = arg_parse_uint(&arg); - else if (arg_match(&arg, &resize_up_thresh, argi)) - cfg.rc_resize_up_thresh = arg_parse_uint(&arg); - else if (arg_match(&arg, &resize_down_thresh, argi)) - cfg.rc_resize_down_thresh = arg_parse_uint(&arg); - else if (arg_match(&arg, &end_usage, argi)) - cfg.rc_end_usage = arg_parse_enum_or_int(&arg); - else if (arg_match(&arg, &target_bitrate, argi)) - cfg.rc_target_bitrate = arg_parse_uint(&arg); - else if (arg_match(&arg, &min_quantizer, argi)) - cfg.rc_min_quantizer = arg_parse_uint(&arg); - else if (arg_match(&arg, &max_quantizer, argi)) - cfg.rc_max_quantizer = arg_parse_uint(&arg); - else if (arg_match(&arg, &undershoot_pct, argi)) - cfg.rc_undershoot_pct = arg_parse_uint(&arg); - else if (arg_match(&arg, &overshoot_pct, argi)) - cfg.rc_overshoot_pct = arg_parse_uint(&arg); - else if (arg_match(&arg, &buf_sz, argi)) - cfg.rc_buf_sz = arg_parse_uint(&arg); - else if (arg_match(&arg, &buf_initial_sz, argi)) - cfg.rc_buf_initial_sz = arg_parse_uint(&arg); - else if (arg_match(&arg, &buf_optimal_sz, argi)) - cfg.rc_buf_optimal_sz = arg_parse_uint(&arg); - else if (arg_match(&arg, &bias_pct, argi)) + do { - cfg.rc_2pass_vbr_bias_pct = arg_parse_uint(&arg); - - if (global.passes < 2) - warn("option %s ignored in one-pass mode.\n", arg.name); - } - else if (arg_match(&arg, &minsection_pct, argi)) - { - cfg.rc_2pass_vbr_minsection_pct = arg_parse_uint(&arg); - - if (global.passes < 2) - warn("option %s ignored in one-pass mode.\n", arg.name); - } - else if (arg_match(&arg, &maxsection_pct, argi)) - { - cfg.rc_2pass_vbr_maxsection_pct = arg_parse_uint(&arg); - - if (global.passes < 2) - warn("option %s ignored in one-pass mode.\n", arg.name); - } - else if (arg_match(&arg, &kf_min_dist, argi)) - cfg.kf_min_dist = arg_parse_uint(&arg); - else if (arg_match(&arg, &kf_max_dist, argi)) - cfg.kf_max_dist = arg_parse_uint(&arg); - else if (arg_match(&arg, &kf_disabled, argi)) - cfg.kf_mode = VPX_KF_DISABLED; - else - argj++; - } - - /* Handle codec specific options */ -#if CONFIG_VP8_ENCODER - - if (global.codec->iface == &vpx_codec_vp8_cx_algo) - { - ctrl_args = vp8_args; - ctrl_args_map = vp8_arg_ctrl_map; - } - -#endif - - for (argi = argj = argv; (*argj = *argi); argi += arg.argv_step) - { - int match = 0; - - arg.argv_step = 1; - - for (i = 0; ctrl_args[i]; i++) - { - if (arg_match(&arg, ctrl_args[i], argi)) - { - int j; - match = 1; - - /* Point either to the next free element or the first - * instance of this control. - */ - for(j=0; j<arg_ctrl_cnt; j++) - if(arg_ctrls[j][0] == ctrl_args_map[i]) - break; - - /* Update/insert */ - assert(j < ARG_CTRL_CNT_MAX); - if (j < ARG_CTRL_CNT_MAX) - { - arg_ctrls[j][0] = ctrl_args_map[i]; - arg_ctrls[j][1] = arg_parse_enum_or_int(&arg); - if(j == arg_ctrl_cnt) - arg_ctrl_cnt++; - } - - } - } - - if (!match) - argj++; + stream = new_stream(&global, stream); + stream_cnt++; + if(!streams) + streams = stream; + } while(parse_stream_params(&global, stream, argv)); } /* Check for unrecognized options */ @@ -1873,28 +2295,39 @@ if (!input.fn) usage_exit(); - if(!global.out_fn) - die("Error: Output file is required (specify with -o)\n"); - - memset(&stats, 0, sizeof(stats)); - for (pass = global.pass ? global.pass - 1 : 0; pass < global.passes; pass++) { - int frames_in = 0, frames_out = 0; - int64_t nbytes = 0; + int frames_in = 0; open_input_file(&input); - /* Update configuration settings parsed from the file */ - if(input.w && input.h) - { - cfg.g_w = input.w; - cfg.g_h = input.h; - } + /* If the input file doesn't specify its w/h (raw files), try to get + * the data from the first stream's configuration. + */ + if(!input.w || !input.h) + FOREACH_STREAM({ + if(stream->config.cfg.g_w && stream->config.cfg.g_h) + { + input.w = stream->config.cfg.g_w; + input.h = stream->config.cfg.g_h; + break; + } + }); - if(!cfg.g_w || !cfg.g_h) - fatal("Specify stream dimensions with --width (-w) " - " and --height (-h).\n"); + /* Update stream configurations from the input file's parameters */ + FOREACH_STREAM(set_stream_dimensions(stream, input.w, input.h)); + FOREACH_STREAM(validate_stream_config(stream)); + + /* Ensure that --passes and --pass are consistent. If --pass is set and + * --passes=2, ensure --fpf was set. + */ + if (global.pass && global.passes == 2) + FOREACH_STREAM({ + if(!stream->config.stats_fn) + die("Stream %d: Must specify --fpf when --pass=%d" + " and --passes=2\n", stream->index, global.pass); + }); + /* Use the frame rate from the file only if none was specified * on the command-line. @@ -1902,48 +2335,9 @@ if (!global.have_framerate) global.framerate = input.framerate; - -#define SHOW(field) fprintf(stderr, " %-28s = %d\n", #field, cfg.field) - + /* Show configuration */ if (global.verbose && pass == 0) - { - fprintf(stderr, "Codec: %s\n", - vpx_codec_iface_name(global.codec->iface)); - fprintf(stderr, "Source file: %s Format: %s\n", input.fn, - global.use_i420 ? "I420" : "YV12"); - fprintf(stderr, "Destination file: %s\n", global.out_fn); - fprintf(stderr, "Encoder parameters:\n"); - - SHOW(g_usage); - SHOW(g_threads); - SHOW(g_profile); - SHOW(g_w); - SHOW(g_h); - SHOW(g_timebase.num); - SHOW(g_timebase.den); - SHOW(g_error_resilient); - SHOW(g_pass); - SHOW(g_lag_in_frames); - SHOW(rc_dropframe_thresh); - SHOW(rc_resize_allowed); - SHOW(rc_resize_up_thresh); - SHOW(rc_resize_down_thresh); - SHOW(rc_end_usage); - SHOW(rc_target_bitrate); - SHOW(rc_min_quantizer); - SHOW(rc_max_quantizer); - SHOW(rc_undershoot_pct); - SHOW(rc_overshoot_pct); - SHOW(rc_buf_sz); - SHOW(rc_buf_initial_sz); - SHOW(rc_buf_optimal_sz); - SHOW(rc_2pass_vbr_bias_pct); - SHOW(rc_2pass_vbr_minsection_pct); - SHOW(rc_2pass_vbr_maxsection_pct); - SHOW(kf_mode); - SHOW(kf_min_dist); - SHOW(kf_max_dist); - } + FOREACH_STREAM(show_stream_config(stream, &global, &input)); if(pass == (global.pass ? global.pass - 1 : 0)) { if (input.file_type == FILE_TYPE_Y4M) @@ -1952,80 +2346,26 @@ frames.*/ memset(&raw, 0, sizeof(raw)); else - vpx_img_alloc(&raw, global.use_i420 ? VPX_IMG_FMT_I420 : VPX_IMG_FMT_YV12, - cfg.g_w, cfg.g_h, 1); + vpx_img_alloc(&raw, + input.use_i420 ? VPX_IMG_FMT_I420 + : VPX_IMG_FMT_YV12, + input.w, input.h, 1); - init_rate_histogram(&rate_hist, &cfg, &global.framerate); + FOREACH_STREAM(init_rate_histogram(&stream->rate_hist, + &stream->config.cfg, + &global.framerate)); } - outfile = strcmp(global.out_fn, "-") ? fopen(global.out_fn, "wb") - : set_binary_mode(stdout); - - if (!outfile) - fatal("Failed to open output file"); - - if(global.write_webm && fseek(outfile, 0, SEEK_CUR)) - fatal("WebM output to pipes not supported."); - - if (global.stats_fn) - { - if (!stats_open_file(&stats, global.stats_fn, pass)) - fatal("Failed to open statistics store"); - } - else - { - if (!stats_open_mem(&stats, pass)) - fatal("Failed to open statistics store"); - } - - cfg.g_pass = global.passes == 2 - ? pass ? VPX_RC_LAST_PASS : VPX_RC_FIRST_PASS - : VPX_RC_ONE_PASS; -#if VPX_ENCODER_ABI_VERSION > (1 + VPX_CODEC_ABI_VERSION) - - if (pass) - { - cfg.rc_twopass_stats_in = stats_get(&stats); - } - -#endif - - if(global.write_webm) - { - ebml.stream = outfile; - write_webm_file_header(&ebml, &cfg, &global.framerate, stereo_fmt); - } - else - write_ivf_file_header(outfile, &cfg, global.codec->fourcc, 0); - - - /* Construct Encoder Context */ - vpx_codec_enc_init(&encoder, global.codec->iface, &cfg, - global.show_psnr ? VPX_CODEC_USE_PSNR : 0); - ctx_exit_on_error(&encoder, "Failed to initialize encoder"); - - /* Note that we bypass the vpx_codec_control wrapper macro because - * we're being clever to store the control IDs in an array. Real - * applications will want to make use of the enumerations directly - */ - for (i = 0; i < arg_ctrl_cnt; i++) - { - if (vpx_codec_control_(&encoder, arg_ctrls[i][0], arg_ctrls[i][1])) - fprintf(stderr, "Error: Tried to set control %d = %d\n", - arg_ctrls[i][0], arg_ctrls[i][1]); - - ctx_exit_on_error(&encoder, "Failed to control codec"); - } + FOREACH_STREAM(open_output_file(stream, &global)); + FOREACH_STREAM(setup_pass(stream, &global, pass)); + FOREACH_STREAM(initialize_encoder(stream, &global)); frame_avail = 1; got_data = 0; while (frame_avail || got_data) { - vpx_codec_iter_t iter = NULL; - const vpx_codec_cx_pkt_t *pkt; struct vpx_usec_timer timer; - int64_t frame_start, next_frame_start; if (!global.limit || frames_in < global.limit) { @@ -2034,157 +2374,80 @@ if (frame_avail) frames_in++; - fprintf(stderr, - "\rPass %d/%d frame %4d/%-4d %7"PRId64"B \033[K", - pass + 1, global.passes, frames_in, frames_out, nbytes); + if(stream_cnt == 1) + fprintf(stderr, + "\rPass %d/%d frame %4d/%-4d %7"PRId64"B \033[K", + pass + 1, global.passes, frames_in, + streams->frames_out, streams->nbytes); + else + fprintf(stderr, + "\rPass %d/%d frame %4d %7lu %s (%.2f fps)\033[K", + pass + 1, global.passes, frames_in, + cx_time > 9999999 ? cx_time / 1000 : cx_time, + cx_time > 9999999 ? "ms" : "us", + usec_to_fps(cx_time, frames_in)); + } else frame_avail = 0; vpx_usec_timer_start(&timer); - - frame_start = (cfg.g_timebase.den * (int64_t)(frames_in - 1) - * global.framerate.den) / cfg.g_timebase.num / global.framerate.num; - next_frame_start = (cfg.g_timebase.den * (int64_t)(frames_in) - * global.framerate.den) - / cfg.g_timebase.num / global.framerate.num; - vpx_codec_encode(&encoder, frame_avail ? &raw : NULL, frame_start, - next_frame_start - frame_start, - 0, global.deadline); + FOREACH_STREAM(encode_frame(stream, &global, + frame_avail ? &raw : NULL, + frames_in)); vpx_usec_timer_mark(&timer); cx_time += vpx_usec_timer_elapsed(&timer); - ctx_exit_on_error(&encoder, "Failed to encode frame"); - if(cfg.g_pass != VPX_RC_FIRST_PASS) - { - int q; - - vpx_codec_control(&encoder, VP8E_GET_LAST_QUANTIZER_64, &q); - ctx_exit_on_error(&encoder, "Failed to read quantizer"); - counts[q]++; - } + FOREACH_STREAM(update_quantizer_histogram(stream)); got_data = 0; - - while ((pkt = vpx_codec_get_cx_data(&encoder, &iter))) - { - got_data = 1; - - switch (pkt->kind) - { - case VPX_CODEC_CX_FRAME_PKT: - frames_out++; - fprintf(stderr, " %6luF", - (unsigned long)pkt->data.frame.sz); - - update_rate_histogram(&rate_hist, &cfg, pkt); - if(global.write_webm) - { - /* Update the hash */ - if(!ebml.debug) - hash = murmur(pkt->data.frame.buf, - pkt->data.frame.sz, hash); - - write_webm_block(&ebml, &cfg, pkt); - } - else - { - write_ivf_frame_header(outfile, pkt); - if(fwrite(pkt->data.frame.buf, 1, - pkt->data.frame.sz, outfile)); - } - nbytes += pkt->data.raw.sz; - break; - case VPX_CODEC_STATS_PKT: - frames_out++; - fprintf(stderr, " %6luS", - (unsigned long)pkt->data.twopass_stats.sz); - stats_write(&stats, - pkt->data.twopass_stats.buf, - pkt->data.twopass_stats.sz); - nbytes += pkt->data.raw.sz; - break; - case VPX_CODEC_PSNR_PKT: - - if (global.show_psnr) - { - int i; - - psnr_sse_total += pkt->data.psnr.sse[0]; - psnr_samples_total += pkt->data.psnr.samples[0]; - for (i = 0; i < 4; i++) - { - fprintf(stderr, "%.3lf ", pkt->data.psnr.psnr[i]); - psnr_totals[i] += pkt->data.psnr.psnr[i]; - } - psnr_count++; - } - - break; - default: - break; - } - } + FOREACH_STREAM(get_cx_data(stream, &global, &got_data)); fflush(stdout); } - fprintf(stderr, - "\rPass %d/%d frame %4d/%-4d %7"PRId64"B %7lub/f %7"PRId64"b/s" - " %7lu %s (%.2f fps)\033[K", pass + 1, - global.passes, frames_in, frames_out, nbytes, - frames_in ? (unsigned long)(nbytes * 8 / frames_in) : 0, - frames_in ? nbytes * 8 *(int64_t)global.framerate.num / global.framerate.den / frames_in : 0, - cx_time > 9999999 ? cx_time / 1000 : cx_time, - cx_time > 9999999 ? "ms" : "us", - cx_time > 0 ? (float)frames_in * 1000000.0 / (float)cx_time : 0); + if(stream_cnt > 1) + fprintf(stderr, "\n"); - if ( (global.show_psnr) && (psnr_count>0) ) - { - int i; - double ovpsnr = vp8_mse2psnr(psnr_samples_total, 255.0, - psnr_sse_total); + FOREACH_STREAM(fprintf( + stderr, + "\rPass %d/%d frame %4d/%-4d %7"PRId64"B %7lub/f %7"PRId64"b/s" + " %7lu %s (%.2f fps)\033[K\n", pass + 1, + global.passes, frames_in, stream->frames_out, stream->nbytes, + frames_in ? (unsigned long)(stream->nbytes * 8 / frames_in) : 0, + frames_in ? stream->nbytes * 8 + * (int64_t)global.framerate.num / global.framerate.den + / frames_in + : 0, + stream->cx_time > 9999999 ? stream->cx_time / 1000 : stream->cx_time, + stream->cx_time > 9999999 ? "ms" : "us", + usec_to_fps(stream->cx_time, frames_in)); + ); - fprintf(stderr, "\nPSNR (Overall/Avg/Y/U/V)"); + if (global.show_psnr) + FOREACH_STREAM(show_psnr(stream)); - fprintf(stderr, " %.3lf", ovpsnr); - for (i = 0; i < 4; i++) - { - fprintf(stderr, " %.3lf", psnr_totals[i]/psnr_count); - } - } - - vpx_codec_destroy(&encoder); + FOREACH_STREAM(vpx_codec_destroy(&stream->encoder)); close_input_file(&input); - if(global.write_webm) - { - write_webm_file_footer(&ebml, hash); - free(ebml.cue_list); - ebml.cue_list = NULL; - } - else - { - if (!fseek(outfile, 0, SEEK_SET)) - write_ivf_file_header(outfile, &cfg, global.codec->fourcc, - frames_out); - } + FOREACH_STREAM(close_output_file(stream, global.codec->fourcc)); - fclose(outfile); - stats_close(&stats, global.passes-1); - fprintf(stderr, "\n"); + FOREACH_STREAM(stats_close(&stream->stats, global.passes-1)); if (global.pass) break; } if (global.show_q_hist_buckets) - show_q_histogram(counts, global.show_q_hist_buckets); + FOREACH_STREAM(show_q_histogram(stream->counts, + global.show_q_hist_buckets)); if (global.show_rate_hist_buckets) - show_rate_histogram(&rate_hist, &cfg, global.show_rate_hist_buckets); - destroy_rate_histogram(&rate_hist); + FOREACH_STREAM(show_rate_histogram(&stream->rate_hist, + &stream->config.cfg, + global.show_rate_hist_buckets)); + FOREACH_STREAM(destroy_rate_histogram(&stream->rate_hist)); vpx_img_free(&raw); free(argv);