While working on cropping video image in my newest version of VideoEditor app, I came to solving an issue of cropping area being incompatible with possible codec’s input. There’re two issues with this matter: cropping an area out of a YUV image, and then feeding codec with that image.

Let’s see, what are the rules, regarding few pixel formats – and coming from aligning color planes:

  • YUV444P and all RGB/BGR formats: no restrictions, as they’re not packed;
  • YUV422P requires x offset to be even, and also the overall width to be even;
  • YUV420P (most common in H.264/H.265 codecs), and similar, like NV12 or YUVJ420P requires both x and y offset to be even, and also both width and height to be even.

Now, the second problem is a bit tricky – and sometimes it is a problem, while sometimes it’s not 😉  But, for encoding with codecs like H.264 or H.265, the safe choice is to have overall height to be divisible by 4 – or even by 16, to be on a super safe side.

So, that’s why I wrote this little helper function:

static QRect makeRectYUVCompliant(const QRect& rect, AVPixelFormat format, bool forceHeight4 = false, bool forceHeight16 = false)
{
    int x = rect.x();
    int y = rect.y();
    int w = rect.width();
    int h = rect.height();

    switch (format)
    {
	  case AV_PIX_FMT_YUV420P:
	  case AV_PIX_FMT_YUVJ420P:
    case AV_PIX_FMT_NV12:
		  if (x % 2 != 0) x--;
		  if (y % 2 != 0) y--;
      if (w % 2 != 0) w--;
      if (h % 2 != 0) h--;
		  break;
    case AV_PIX_FMT_YUV422P:
        if (x % 2 != 0) x--;
        if (w % 2 != 0) w--;
		break;
	  case AV_PIX_FMT_YUV444P:
	  case AV_PIX_FMT_RGB24:
	  case AV_PIX_FMT_BGR24:
	  case AV_PIX_FMT_ARGB:
	  case AV_PIX_FMT_ABGR:
	  case AV_PIX_FMT_RGBA:
	  case AV_PIX_FMT_BGRA:
		// no requirements
        break;
    default:
        // unknown format, assume YUV420P
        if (x % 2 != 0) x--;
        if (y % 2 != 0) y--;
        if (w % 2 != 0) w--;
        if (h % 2 != 0) h--;
        break;
    }

    if (forceHeight4)
    {
        while (h % 4 != 0) h--;
	  }
    if (forceHeight16)
    {
        while (h % 16 != 0) h--;
	  }

    return QRect(x, y, w, h);
}

Soon I’ll probably write more on the topic of cropping – and how to achieve it, using libavfilter library from ffmpeg.