Hi,

I'm using i.MX6 CODA H.264 encoder and found a minor bug somewhere.
Not sure how it should be fixed, though.
The problem manifests itself when I configure (open, qbuf etc) the
encoder device and then close it without any start/stop streaming calls.
I'm using 2 buffers in this example:

vb2:   counters for queue b699c808, buffer 0: UNBALANCED!
vb2:     buf_init: 1 buf_cleanup: 1 buf_prepare: 1 buf_finish: 1
vb2:     buf_queue: 0 buf_done: 0
vb2:     alloc: 1 put: 1 prepare: 1 finish: 0 mmap: 1
                         ^^^^^^^^^^^^^^^^^^^^
vb2:     get_userptr: 0 put_userptr: 0
vb2:     attach_dmabuf: 0 detach_dmabuf: 0 map_dmabuf: 0 unmap_dmabuf: 0
vb2:     get_dmabuf: 0 num_users: 0 vaddr: 0 cookie: 0

vb2:   counters for queue b699c808, buffer 1: UNBALANCED!
vb2:     buf_init: 1 buf_cleanup: 1 buf_prepare: 1 buf_finish: 1
vb2:     buf_queue: 0 buf_done: 0
vb2:     alloc: 1 put: 1 prepare: 1 finish: 0 mmap: 1
                         ^^^^^^^^^^^^^^^^^^^^
vb2:     get_userptr: 0 put_userptr: 0
vb2:     attach_dmabuf: 0 detach_dmabuf: 0 map_dmabuf: 0 unmap_dmabuf: 0
vb2:     get_dmabuf: 0 num_users: 0 vaddr: 0 cookie: 0

These are H.264 (encoder "capture") buffers. Note the alloc
prepare/finish disparity.

I have investigated a bit and it seems it's some missing *buf_done()
call, probably belonging to coda_release(), but I'm not sure. Or maybe
should my program "finish" the buffers before doing close()?

Linux 4.13. I'm using CONFIG_VIDEO_ADV_DEBUG=y.
Compile with:
arm-pc-linux-gnueabihf-gcc -std=gnu99 -O2 -W -Wall -Wno-sign-compare 
-Wno-missing-field-initializers -D_GNU_SOURCE coda_test.c -o coda_test
Any other details are, of course, available.

Ideas?
--
Krzysztof Halasa

Industrial Research Institute for Automation and Measurements PIAP
Al. Jerozolimskie 202, 02-486 Warsaw, Poland


// -*- mode: c; c-basic-offset: 4; tab-width: 4; -*-
// coda_test.c
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <linux/videodev2.h>
#include <sys/mman.h>
#include <sys/ioctl.h>

static inline int doioctl(int fd, unsigned long req, void *ptr)
{
        return TEMP_FAILURE_RETRY(ioctl(fd, req, ptr));
}

void *xmalloc(size_t size)
{
        void *ptr = malloc(size);
        if (!ptr) {
                printf("Unable to allocate %zu bytes of data\n", size);
                                exit(1);
                }

        return ptr;
}

static void print_pixel_format(const char *name, struct v4l2_pix_format *pix)
{
        printf("%s %u x %u ", name, pix->width, pix->height);

        const char *ptr = (void*)&pix->pixelformat;
        for (int i = 0; i < 4; i++) {
                if ((ptr[i] >= 'A' && ptr[i] <= 'Z') ||
                        (ptr[i] >= 'a' && ptr[i] <= 'z') ||
                        (ptr[i] >= '0' && ptr[i] <= '9'))
                        printf("%c", ptr[i]);
                else
                        printf("?");
        }

        switch (pix->field) {
        case V4L2_FIELD_NONE:
                break;
        case V4L2_FIELD_TOP:
                printf(" top field");
                break;
        case V4L2_FIELD_BOTTOM:
                printf(" bottom field");
                break;
        case V4L2_FIELD_INTERLACED:
                printf(" interlaced");
                break;
        case V4L2_FIELD_SEQ_TB:
                printf(" top field first");
                break;
        case V4L2_FIELD_SEQ_BT:
                printf(" bottom field first");
                break;
        default:
                printf(" field %u", pix->field);
                break;
        }
        printf(" frame size %u\n", pix->sizeimage);
}

int main(void)
{
        struct {
                void *start;
                size_t length;
        } *h264_buffers = NULL;

        int fd;

        if ((fd = open("/dev/coda-encoder", O_RDWR, 0)) < 0) {
                printf("Unable to open video encoder device: %m\n");
                return -1;
        }

        struct v4l2_format encoder_fmt = {
                .type = V4L2_BUF_TYPE_VIDEO_OUTPUT,
                .fmt.pix.pixelformat = V4L2_PIX_FMT_NV12,
                .fmt.pix.width = 704,
                .fmt.pix.height = 576,
                .fmt.pix.field = V4L2_FIELD_NONE,
        };
        if (doioctl(fd, VIDIOC_S_FMT, &encoder_fmt) < 0) {
                printf("Unable to set video encoder sink format\n");
                goto close_streams;
        }

        print_pixel_format("Encoder output:", &encoder_fmt.fmt.pix);

        encoder_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        if (doioctl(fd, VIDIOC_G_FMT, &encoder_fmt) < 0) {
                printf("Unable to get video encoder capture format\n");
                goto close_streams;
        }

        print_pixel_format("Encoder input:", &encoder_fmt.fmt.pix);

        // Query buffer counts
        struct v4l2_requestbuffers req = {
                .count = 2,
                .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
                .memory = V4L2_MEMORY_MMAP,
        };

        if (doioctl(fd, VIDIOC_REQBUFS, &req) < 0) {
                printf("Unable to request h.264 buffer settings: %m\n");
                goto close_streams;
        }

        printf("Using %u h.264 buffers\n", req.count);
        h264_buffers = xmalloc(req.count * sizeof(h264_buffers[0]));

        sleep(1);
        for (int cnt = 0; cnt < req.count; cnt++) {
                struct v4l2_buffer buf = {
                        .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
                        .memory = V4L2_MEMORY_MMAP,
                        .index = cnt,
                };
                if (doioctl(fd, VIDIOC_QUERYBUF, &buf) < 0) {
                        printf("Unable to query video encoder output buffer 
#%u: %m\n", cnt);
                        goto close_streams;
                }

                void *ptr = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, 
MAP_SHARED, fd, buf.m.offset);
                if (ptr == MAP_FAILED) {
                        printf("Unable to map video encoder output buffer: 
%m\n");
                        goto close_streams;
                }
                h264_buffers[cnt].length = buf.length;
                h264_buffers[cnt].start = ptr;
                printf("mmap %p %u\n", h264_buffers[cnt].start, 
h264_buffers[cnt].length);
        }

        for (int cnt = 0; cnt < req.count; cnt++) {
                struct v4l2_buffer buf = {
                        .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
                        .memory = V4L2_MEMORY_MMAP,
                        .index = cnt,
                };
                if (doioctl(fd, VIDIOC_QBUF, &buf) < 0) {
                        printf("Unable to queue video encoder output buffer: 
%m\n");
                        goto close_streams;
                }
        }

close_streams:
        printf("Freeing %u h.264 buffers\n", req.count);
        sleep(1);
        for (int cnt = 0; cnt < req.count; cnt++)
                if (h264_buffers && h264_buffers[cnt].start) {
                        printf("munmap %p %u\n", h264_buffers[cnt].start, 
h264_buffers[cnt].length);
                        munmap(h264_buffers[cnt].start, 
h264_buffers[cnt].length);
                }
        free(h264_buffers);
        printf("Closing encoder device\n");
        sleep(1);
        close(fd);

        printf("Exiting\n");
        sleep(1);
        return -1;
}

Reply via email to