Motion Vector with the H.264 Encoder

Astra SDK scarthgap_6.12_v2.2.0 adds support for accessing motion vectors from the H.264 encoder used by SL1640 and SL1680.

The vpu driver adds a new control SYNA_V4L2_CID_VENC_ENABLE_METADATA_MV which can be set to enable motion vector data. https://github.com/synaptics-astra/linux_6_12-drivers-synaptics/commit/fd6bca6bf70637aa267d2d36159f05ecfa97a979

This example sets the V4L2 Control ID and Value:

ctrl.id = SYNA_V4L2_CID_VENC_ENABLE_METADATA_MV;
ctrl.value = true;
dbg("setting (%d): %u", id, ctrl.value);
ret = ::ioctl(mFd, VIDIOC_S_CTRL, &ctrl);

Motion Vector Data can be accessed from the capture buffer:

v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret = ::ioctrl(mFd, VIDIOC_DQBUF, &v4l2_buf);
v4l2_buf plane[0]:  encoded ES data
v4l2_buf plane[1]:  motion vector data

This example prints the Motion Vector data to a file:

int write_motion_vector(void *mv,                /* plane[1] address */
                        size_t size,             /* plane[1]payload */
                        uint32_t frameIdx,       /* v4l2_buf sequence */
                        uint32_t width, uint32_t height)
{
    uint32_t i;
    uint32_t mbPerRow = (width + 15) / 16;
    uint32_t mbPerCol = (height + 15) / 16;
    uint32_t mbPerFrame = mbPerRow * mbPerCol;
    MVInfo_t *mbInfo = (MVInfo_t *)mv;
    if (!mFp || !mv)
        return 0;
    /* Print motion vector for every macroblock. */
    fprintf(mFp, "\npic=%d  MV full-pixel X,Y "
            "for %d macroblocks (%dx%d) block=0\n",
            frameIdx, mbPerFrame, mbPerRow, mbPerCol);
    for (i = 0; i < mbPerFrame; i++) {
        uint32_t mb_type = mbInfo[i].mbType;
        uint32_t mb_y = i / mbPerRow;
        uint32_t mb_x = i % mbPerRow;
        uint32_t sx = mb_x * 16;
        uint32_t sy = mb_y * 16;
        fprintf(mFp, " MBxy:(%3d,%3d) Type:%8s ", sx, sy, mbModetostr(mb_type));
        if (mb_type == 6) { // P16x16
            fprintf(mFp, "MV(%3d,%3d )[%21s]", mbInfo[i].mvX[0], mbInfo[i].mvY[0], " ");
        } else if (mb_type == 7 || mb_type == 8) { // P16x8, P8x16
            fprintf(mFp, "MV(%3d,%3d ", mbInfo[i].mvX[0], mbInfo[i].mvY[0]);
            fprintf(mFp, "%3d,%3d) [%13s]", mbInfo[i].mvX[1], mbInfo[i].mvY[1], " ");
        } else if (mb_type == 9) { // P8x8
            fprintf(mFp, "MV(%3d,%3d ", mbInfo[i].mvX[0], mbInfo[i].mvY[0]);
            fprintf(mFp, "%3d,%3d ", mbInfo[i].mvX[1], mbInfo[i].mvY[1]);
            fprintf(mFp, "%3d,%3d ", mbInfo[i].mvX[2], mbInfo[i].mvY[2]);
            fprintf(mFp, "%3d,%3d)", mbInfo[i].mvX[3], mbInfo[i].mvY[3]);
        } else {
            fprintf(mFp, "MV[%31s]", " ");
        }
        if ((i % mbPerRow) == mbPerRow-1) fprintf(mFp, "\n");
    }
    fflush(mFp);
    return 0;
}