static void pred_spatial_direct_motion(const H264Context *const h, H264SliceContext *sl,

                                       int *mb_type)

{

    int b8_stride = 2;

    int b4_stride = h->b_stride;

    int mb_xy = sl->mb_xy, mb_y = sl->mb_y;

    int mb_type_col[2];

    const int16_t (*l1mv0)[2], (*l1mv1)[2];

    const int8_t *l1ref0, *l1ref1;

    const int is_b8x8 = IS_8X8(*mb_type);

    unsigned int sub_mb_type = MB_TYPE_L0L1;

    int i8, i4;

    int ref[2];

    int mv[2];

    int list;



    assert(sl->ref_list[1][0].reference & 3);



    await_reference_mb_row(h, sl->ref_list[1][0].parent,

                           sl->mb_y + !!IS_INTERLACED(*mb_type));



#define MB_TYPE_16x16_OR_INTRA (MB_TYPE_16x16 | MB_TYPE_INTRA4x4 | \

                                MB_TYPE_INTRA16x16 | MB_TYPE_INTRA_PCM)



    /* ref = min(neighbors) */

    for (list = 0; list < 2; list++) {

        int left_ref     = sl->ref_cache[list][scan8[0] - 1];

        int top_ref      = sl->ref_cache[list][scan8[0] - 8];

        int refc         = sl->ref_cache[list][scan8[0] - 8 + 4];

        const int16_t *C = sl->mv_cache[list][scan8[0]  - 8 + 4];

        if (refc == PART_NOT_AVAILABLE) {

            refc = sl->ref_cache[list][scan8[0] - 8 - 1];

            C    = sl->mv_cache[list][scan8[0]  - 8 - 1];

        }

        ref[list] = FFMIN3((unsigned)left_ref,

                           (unsigned)top_ref,

                           (unsigned)refc);

        if (ref[list] >= 0) {

            /* This is just pred_motion() but with the cases removed that

             * cannot happen for direct blocks. */

            const int16_t *const A = sl->mv_cache[list][scan8[0] - 1];

            const int16_t *const B = sl->mv_cache[list][scan8[0] - 8];



            int match_count = (left_ref == ref[list]) +

                              (top_ref  == ref[list]) +

                              (refc     == ref[list]);



            if (match_count > 1) { // most common

                mv[list] = pack16to32(mid_pred(A[0], B[0], C[0]),

                                      mid_pred(A[1], B[1], C[1]));

            } else {

                assert(match_count == 1);

                if (left_ref == ref[list])

                    mv[list] = AV_RN32A(A);

                else if (top_ref == ref[list])

                    mv[list] = AV_RN32A(B);

                else

                    mv[list] = AV_RN32A(C);

            }

        } else {

            int mask = ~(MB_TYPE_L0 << (2 * list));

            mv[list]  = 0;

            ref[list] = -1;

            if (!is_b8x8)

                *mb_type &= mask;

            sub_mb_type &= mask;

        }

    }

    if (ref[0] < 0 && ref[1] < 0) {

        ref[0] = ref[1] = 0;

        if (!is_b8x8)

            *mb_type |= MB_TYPE_L0L1;

        sub_mb_type |= MB_TYPE_L0L1;

    }



    if (!(is_b8x8 | mv[0] | mv[1])) {

        fill_rectangle(&sl->ref_cache[0][scan8[0]], 4, 4, 8, (uint8_t)ref[0], 1);

        fill_rectangle(&sl->ref_cache[1][scan8[0]], 4, 4, 8, (uint8_t)ref[1], 1);

        fill_rectangle(&sl->mv_cache[0][scan8[0]], 4, 4, 8, 0, 4);

        fill_rectangle(&sl->mv_cache[1][scan8[0]], 4, 4, 8, 0, 4);

        *mb_type = (*mb_type & ~(MB_TYPE_8x8 | MB_TYPE_16x8 | MB_TYPE_8x16 |

                                 MB_TYPE_P1L0 | MB_TYPE_P1L1)) |

                   MB_TYPE_16x16 | MB_TYPE_DIRECT2;

        return;

    }



    if (IS_INTERLACED(sl->ref_list[1][0].parent->mb_type[mb_xy])) { // AFL/AFR/FR/FL -> AFL/FL

        if (!IS_INTERLACED(*mb_type)) {                    //     AFR/FR    -> AFL/FL

            mb_y  = (sl->mb_y & ~1) + sl->col_parity;

            mb_xy = sl->mb_x +

                    ((sl->mb_y & ~1) + sl->col_parity) * h->mb_stride;

            b8_stride = 0;

        } else {

            mb_y  += sl->col_fieldoff;

            mb_xy += h->mb_stride * sl->col_fieldoff; // non-zero for FL -> FL & differ parity

        }

        goto single_col;

    } else {                                             // AFL/AFR/FR/FL -> AFR/FR

        if (IS_INTERLACED(*mb_type)) {                   // AFL       /FL -> AFR/FR

            mb_y           =  sl->mb_y & ~1;

            mb_xy          = (sl->mb_y & ~1) * h->mb_stride + sl->mb_x;

            mb_type_col[0] = sl->ref_list[1][0].parent->mb_type[mb_xy];

            mb_type_col[1] = sl->ref_list[1][0].parent->mb_type[mb_xy + h->mb_stride];

            b8_stride      = 2 + 4 * h->mb_stride;

            b4_stride     *= 6;

            if (IS_INTERLACED(mb_type_col[0]) !=

                IS_INTERLACED(mb_type_col[1])) {

                mb_type_col[0] &= ~MB_TYPE_INTERLACED;

                mb_type_col[1] &= ~MB_TYPE_INTERLACED;

            }



            sub_mb_type |= MB_TYPE_16x16 | MB_TYPE_DIRECT2; /* B_SUB_8x8 */

            if ((mb_type_col[0] & MB_TYPE_16x16_OR_INTRA) &&

                (mb_type_col[1] & MB_TYPE_16x16_OR_INTRA) &&

                !is_b8x8) {

                *mb_type |= MB_TYPE_16x8 | MB_TYPE_DIRECT2;  /* B_16x8 */

            } else {

                *mb_type |= MB_TYPE_8x8;

            }

        } else {                                         //     AFR/FR    -> AFR/FR

single_col:

            mb_type_col[0] =

            mb_type_col[1] = sl->ref_list[1][0].parent->mb_type[mb_xy];



            sub_mb_type |= MB_TYPE_16x16 | MB_TYPE_DIRECT2; /* B_SUB_8x8 */

            if (!is_b8x8 && (mb_type_col[0] & MB_TYPE_16x16_OR_INTRA)) {

                *mb_type |= MB_TYPE_16x16 | MB_TYPE_DIRECT2; /* B_16x16 */

            } else if (!is_b8x8 &&

                       (mb_type_col[0] & (MB_TYPE_16x8 | MB_TYPE_8x16))) {

                *mb_type |= MB_TYPE_DIRECT2 |

                            (mb_type_col[0] & (MB_TYPE_16x8 | MB_TYPE_8x16));

            } else {

                if (!h->sps.direct_8x8_inference_flag) {

                    /* FIXME: Save sub mb types from previous frames (or derive

                     * from MVs) so we know exactly what block size to use. */

                    sub_mb_type += (MB_TYPE_8x8 - MB_TYPE_16x16); /* B_SUB_4x4 */

                }

                *mb_type |= MB_TYPE_8x8;

            }

        }

    }



    await_reference_mb_row(h, sl->ref_list[1][0].parent, mb_y);



    l1mv0  = &sl->ref_list[1][0].parent->motion_val[0][h->mb2b_xy[mb_xy]];

    l1mv1  = &sl->ref_list[1][0].parent->motion_val[1][h->mb2b_xy[mb_xy]];

    l1ref0 = &sl->ref_list[1][0].parent->ref_index[0][4 * mb_xy];

    l1ref1 = &sl->ref_list[1][0].parent->ref_index[1][4 * mb_xy];

    if (!b8_stride) {

        if (sl->mb_y & 1) {

            l1ref0 += 2;

            l1ref1 += 2;

            l1mv0  += 2 * b4_stride;

            l1mv1  += 2 * b4_stride;

        }

    }



    if (IS_INTERLACED(*mb_type) != IS_INTERLACED(mb_type_col[0])) {

        int n = 0;

        for (i8 = 0; i8 < 4; i8++) {

            int x8  = i8 & 1;

            int y8  = i8 >> 1;

            int xy8 = x8     + y8 * b8_stride;

            int xy4 = x8 * 3 + y8 * b4_stride;

            int a, b;



            if (is_b8x8 && !IS_DIRECT(sl->sub_mb_type[i8]))

                continue;

            sl->sub_mb_type[i8] = sub_mb_type;



            fill_rectangle(&sl->ref_cache[0][scan8[i8 * 4]], 2, 2, 8,

                           (uint8_t)ref[0], 1);

            fill_rectangle(&sl->ref_cache[1][scan8[i8 * 4]], 2, 2, 8,

                           (uint8_t)ref[1], 1);

            if (!IS_INTRA(mb_type_col[y8]) && !sl->ref_list[1][0].parent->long_ref &&

                ((l1ref0[xy8] == 0 &&

                  FFABS(l1mv0[xy4][0]) <= 1 &&

                  FFABS(l1mv0[xy4][1]) <= 1) ||

                 (l1ref0[xy8] < 0 &&

                  l1ref1[xy8] == 0 &&

                  FFABS(l1mv1[xy4][0]) <= 1 &&

                  FFABS(l1mv1[xy4][1]) <= 1))) {

                a =

                b = 0;

                if (ref[0] > 0)

                    a = mv[0];

                if (ref[1] > 0)

                    b = mv[1];

                n++;

            } else {

                a = mv[0];

                b = mv[1];

            }

            fill_rectangle(&sl->mv_cache[0][scan8[i8 * 4]], 2, 2, 8, a, 4);

            fill_rectangle(&sl->mv_cache[1][scan8[i8 * 4]], 2, 2, 8, b, 4);

        }

        if (!is_b8x8 && !(n & 3))

            *mb_type = (*mb_type & ~(MB_TYPE_8x8 | MB_TYPE_16x8 | MB_TYPE_8x16 |

                                     MB_TYPE_P1L0 | MB_TYPE_P1L1)) |

                       MB_TYPE_16x16 | MB_TYPE_DIRECT2;

    } else if (IS_16X16(*mb_type)) {

        int a, b;



        fill_rectangle(&sl->ref_cache[0][scan8[0]], 4, 4, 8, (uint8_t)ref[0], 1);

        fill_rectangle(&sl->ref_cache[1][scan8[0]], 4, 4, 8, (uint8_t)ref[1], 1);

        if (!IS_INTRA(mb_type_col[0]) && !sl->ref_list[1][0].parent->long_ref &&

            ((l1ref0[0] == 0 &&

              FFABS(l1mv0[0][0]) <= 1 &&

              FFABS(l1mv0[0][1]) <= 1) ||

             (l1ref0[0] < 0 && !l1ref1[0] &&

              FFABS(l1mv1[0][0]) <= 1 &&

              FFABS(l1mv1[0][1]) <= 1 &&

              h->x264_build > 33U))) {

            a = b = 0;

            if (ref[0] > 0)

                a = mv[0];

            if (ref[1] > 0)

                b = mv[1];

        } else {

            a = mv[0];

            b = mv[1];

        }

        fill_rectangle(&sl->mv_cache[0][scan8[0]], 4, 4, 8, a, 4);

        fill_rectangle(&sl->mv_cache[1][scan8[0]], 4, 4, 8, b, 4);

    } else {

        int n = 0;

        for (i8 = 0; i8 < 4; i8++) {

            const int x8 = i8 & 1;

            const int y8 = i8 >> 1;



            if (is_b8x8 && !IS_DIRECT(sl->sub_mb_type[i8]))

                continue;

            sl->sub_mb_type[i8] = sub_mb_type;



            fill_rectangle(&sl->mv_cache[0][scan8[i8 * 4]], 2, 2, 8, mv[0], 4);

            fill_rectangle(&sl->mv_cache[1][scan8[i8 * 4]], 2, 2, 8, mv[1], 4);

            fill_rectangle(&sl->ref_cache[0][scan8[i8 * 4]], 2, 2, 8,

                           (uint8_t)ref[0], 1);

            fill_rectangle(&sl->ref_cache[1][scan8[i8 * 4]], 2, 2, 8,

                           (uint8_t)ref[1], 1);



            assert(b8_stride == 2);

            /* col_zero_flag */

            if (!IS_INTRA(mb_type_col[0]) && !sl->ref_list[1][0].parent->long_ref &&

                (l1ref0[i8] == 0 ||

                 (l1ref0[i8] < 0 &&

                  l1ref1[i8] == 0 &&

                  h->x264_build > 33U))) {

                const int16_t (*l1mv)[2] = l1ref0[i8] == 0 ? l1mv0 : l1mv1;

                if (IS_SUB_8X8(sub_mb_type)) {

                    const int16_t *mv_col = l1mv[x8 * 3 + y8 * 3 * b4_stride];

                    if (FFABS(mv_col[0]) <= 1 && FFABS(mv_col[1]) <= 1) {

                        if (ref[0] == 0)

                            fill_rectangle(&sl->mv_cache[0][scan8[i8 * 4]], 2, 2,

                                           8, 0, 4);

                        if (ref[1] == 0)

                            fill_rectangle(&sl->mv_cache[1][scan8[i8 * 4]], 2, 2,

                                           8, 0, 4);

                        n += 4;

                    }

                } else {

                    int m = 0;

                    for (i4 = 0; i4 < 4; i4++) {

                        const int16_t *mv_col = l1mv[x8 * 2 + (i4 & 1) +

                                                     (y8 * 2 + (i4 >> 1)) * b4_stride];

                        if (FFABS(mv_col[0]) <= 1 && FFABS(mv_col[1]) <= 1) {

                            if (ref[0] == 0)

                                AV_ZERO32(sl->mv_cache[0][scan8[i8 * 4 + i4]]);

                            if (ref[1] == 0)

                                AV_ZERO32(sl->mv_cache[1][scan8[i8 * 4 + i4]]);

                            m++;

                        }

                    }

                    if (!(m & 3))

                        sl->sub_mb_type[i8] += MB_TYPE_16x16 - MB_TYPE_8x8;

                    n += m;

                }

            }

        }

        if (!is_b8x8 && !(n & 15))

            *mb_type = (*mb_type & ~(MB_TYPE_8x8 | MB_TYPE_16x8 | MB_TYPE_8x16 |

                                     MB_TYPE_P1L0 | MB_TYPE_P1L1)) |

                       MB_TYPE_16x16 | MB_TYPE_DIRECT2;

    }

}
