javafmj

Creating a YUVFormat instance


I would like to create a jmf/fmj YUVFormat instance for a dynamically created CaptureDevice using YUV420. I am confused as to what the values are supposed to be for strideY, strideUV, offsetY, offsetU, and offsetV. Only the following constructors are available in the YUVFormat class:

1. YUVFormat()
2. YUVFormat(int yuvType)
3. YUVFormat(java.awt.Dimension size, int maxDataLength, java.lang.Class dataType, float frameRate, int yuvType, int strideY, int strideUV, int offsetY, int offsetU, int offsetV)

Using #1 or #2 doesn't allow me to set size, frame rate, or data type after the fact; so I can't use them. Using #3 requires me to know the five additional parameters. I've read all the following posts from my google search, but I'm still confused as to what the values should be. I think that I can safely assume the strideY and strideUV will be the width of the frame, but I'm not 100% sure.

Javadoc: http://fmj-sf.net/doc/fmj/javax/media/format/YUVFormat.html

MediaWiki: http://wiki.multimedia.cx/index.php?title=PIX_FMT_YUV420P

FourCC: http://www.fourcc.org/yuv.php#IYUV

Here's my code so far:

int strideY = width, strideUV = width / 2;
int offsetY = 0, offsetU = 0, offsetV = 0;
YUVFormat yuv = new YUVFormat(new Dimension(width, height), Format.NOT_SPECIFIED, Format.byteArray, frameRate, YUVFormat.YUV_420, strideY, strideUV, offsetY, offsetU, offsetV);

Solution

  • Last time I used those classes I had memory issues from them internally.

    The format should not really need data rate or frame rate. It merely specifies how pixels are arranged in memory.

    I would advise to handle the bytes in arrays if possible.

    Think of RGBA data. Each word in the memory is 4 pixels. [RGBA][RGBA]... And typically it writes out the bottom left first, and ends at the top right. The size of data is easy to know and specific pixels easy to manipulate.

    YUV is a planar or semi planar format with 12 bits per pixel on average rather than 32 bits. This is achieved by having 8 bits Y and 8 bits U and V with the U and V double sized. The 8 bits of U and V cover 4 pixels of the Y plane.

    So if the picture size is 320 by 240, the first 320 * 240 bytes will be the Y-plane data.

    The next bytes in memory are either interlaced U/V lines as semi planar or all planar with first all U then all V data.

    The stride of Y is the width. The stride of U/V is half the width. The offset of Y is the number of bytes between pixel rows/strides. The offset of U is the number of bytes between pixel rows/strides. The offset of V is the number of bytes between pixel rows/strides.

    They also have 'base address' which is not exposed in java. The memory address of the first Y pixel data.

    On systems that can only allocate 32 bit words of memory as a minimum, images using 12 bit color depth or odd pixel sizes can make the host system behave in different ways regarding where the pixel data resides in addressed memory.

    for instance, write all the Y data packed, it will have a zero offset. Next write one horizontal line of U data. Next write one horizontal line of V data. Next write one horizontal line of U data. Next write one horizontal line of V data.

    The stride of U and V are half the stride of Y.

    In java, you should be able to use zero offsets by writing pixel data without gaps between U and V data.

    The other format of yuv writes all the U and then all the V data in full chunks.

    The offset corresponds to the number of bytes between single Y/U/V rows.

    Base address would correspond to the starting address of the U/V planes.

    data starts 'here(base)' is this 'wide(stride)' with the next row starting there(offset)

    With java the base address is likely given.

    Probably didnt answer the question lol

    {
        unsigned int planeSize;
        unsigned int halfWidth;
    
        unsigned char * yplane;
        unsigned char * uplane;
        unsigned char * vplane;
        const unsigned char * rgbIndex;
    
        int x, y;
        unsigned char * yline;
        unsigned char * uline;
        unsigned char * vline;
    
        planeSize = srcFrameWidth * srcFrameHeight;
        halfWidth = srcFrameWidth >> 1;
    
        // get pointers to the data
        yplane = yuv;
        uplane = yuv + planeSize;
        vplane = yuv + planeSize + (planeSize >> 2);
        rgbIndex = rgb;
    
            for (y = 0; y < srcFrameHeight; y++)
            {
            yline = yplane + (y * srcFrameWidth);
            uline = uplane + ((y >> 1) * halfWidth);
            vline = vplane + ((y >> 1) * halfWidth);
    
            if (flip)
            rgbIndex = rgb + (srcFrameWidth*(srcFrameHeight-1-y)*rgbIncrement);
    
                for (x = 0; x < (int) srcFrameWidth; x+=2)
                {
                    rgbtoyuv(rgbIndex[0], rgbIndex[1], rgbIndex[2], *yline, *uline, *vline);
                    rgbIndex += rgbIncrement;
                    yline++;
                    rgbtoyuv(rgbIndex[0], rgbIndex[1], rgbIndex[2], *yline, *uline, *vline);
                    rgbIndex += rgbIncrement;
                    yline++;
                    uline++;
                    vline++;
    }
    }
    }
    

    In java..

    public static byte[] YV12toYUV420Planar(byte[] input, byte[] output, int width, int height) {
        final int frameSize = width * height;
        final int qFrameSize = frameSize/4;
    
        System.arraycopy(input, 0, output, 0, frameSize); // Y
        System.arraycopy(input, frameSize, output, frameSize + qFrameSize, qFrameSize); // Cr (V)
        System.arraycopy(input, frameSize + qFrameSize, output, frameSize, qFrameSize); // Cb (U)
    
        return output;
    }