1. 程式人生 > 實用技巧 >libyuv實現通用的cvt,resize,crop功能

libyuv實現通用的cvt,resize,crop功能

我實現的思路是所有的格式先轉成i420,然後進行crop resize 以及cvt的操作。

Get Fourcc code

uint32_t Scaler::GetFOURCC(const Scaler::Buffer *src) {
  uint32_t fourcc = 0;
  
  switch (src->color) {
    case Scaler::ColorFormat::YUV_I420:
      fourcc = libyuv::FourCC::FOURCC_I420;
      break;
    
    case Scaler::ColorFormat::YUV_NV21:
      fourcc = libyuv::FourCC::FOURCC_NV21;
      break;
    
    case Scaler::ColorFormat::YUV_NV12:
      fourcc = libyuv::FourCC::FOURCC_NV12;
      break;
    
    case Scaler::ColorFormat::BGR:
      fourcc = libyuv::FourCC::FOURCC_24BG;
      break;

    case Scaler::ColorFormat::RGB:
      fourcc = libyuv::FourCC::FOURCC_RAW;
      break;
    
    case Scaler::ColorFormat::BGRA:
      fourcc = libyuv::FourCC::FOURCC_ARGB;
      break;
    
    case Scaler::ColorFormat::ARGB:
      fourcc = libyuv::FourCC::FOURCC_BGRA;
      break;
    
    case Scaler::ColorFormat::RGBA:
      fourcc = libyuv::FourCC::FOURCC_ABGR;
      break;

    case Scaler::ColorFormat::ABGR:
      fourcc = libyuv::FourCC::FOURCC_RGBA;
      break;

    default:
      LOG(ERROR) << "libyuv not support this color cvt I420";
      break;
  }
  return fourcc;
}

Process

bool LibyuvProcess(const Scaler::Buffer *src, Scaler::Buffer *dst, const Scaler::Rect *crop) {
  if (!src || !dst) return false;

  if (!crop && src->width == dst->width && src->height == dst->height && src->color == dst->color) {
    // copy src to dst directly 
    bool src_continus = IsBufferContinus_(src);
    bool dst_continus = IsBufferContinus_(dst);
    if (src->color <= Scaler::ColorFormat::YUV_I420) {
      if (src_continus && dst_continus) {
        memcpy(dst->data[0], src->data[0], src->width * src->height * 3 / 2);
      } else {
        memcpy(dst->data[0], src->data[0], src->width * src->height);
        memcpy(dst->data[1], src->data[1], src->width * src->height / 4);
        memcpy(dst->data[2], src->data[2], src->width * src->height / 4);
      }

    } else if (src->color <= Scaler::ColorFormat::YUV_NV12) {
      if (src_continus && dst_continus) {
         memcpy(dst->data[0], src->data[0], src->width * src->height * 3 / 2);
      } else {
         memcpy(dst->data[0], src->data[0], src->width * src->height);
         memcpy(dst->data[1], src->data[1], src->width * src->height / 2); 
      }

    } else if (src->color <= Scaler::ColorFormat::RGB) {
      memcpy(dst->data[0], src->data[0], src->width * src->height * 3);
      dst->data[1] = dst->data[2] = nullptr;
    } else if (src->color <= Scaler::ColorFormat::ARGB) {
      memcpy(dst->data[0], src->data[0], src->width * src->height * 4);
      dst->data[1] = dst->data[2] = nullptr;
    } else {
      LOG(ERROR) << "scaler, we not realize this color space" << src->color;
      return false;
    }
    for (size_t i = 0; i < 3; i++) {
      dst->stride[i] = src->stride[i];
    }
    return true;
  } 
  /*
    // TODO after firist, "else if" is test , will be move future
    if (src->color == Scaler::ColorFormat::BGR && dst->color == Scaler::ColorFormat::YUV_NV21) {
      Scaler::LibyuvCvtBGRToNV21_Process(src, dst, crop);
    } else if (src->color == ColorFormat::YUV_NV21 && dst->color == ColorFormat::YUV_NV21) {
      Scaler::LibyuvResize_NV21(src, dst);
    } else if (src->color == ColorFormat::BGR && dst->color == ColorFormat::YUV_I420) {
      Scaler::LibyuvCvtBGRtoI420(src, dst);
    } else if (src->color == ColorFormat::YUV_I420 && dst->color == ColorFormat::YUV_I420) {
      Scaler::LibyuvResize_I420(src, dst);
    }
  */
    uint32_t width = src->width;
    uint32_t height = src->height;
    Scaler::Buffer *src_crop = nullptr;
    Scaler::Buffer *resize_buf = nullptr;
    if (crop != nullptr) {
      width = crop->w;
      height = crop->h;
      if (width == dst->width && height == dst->height && src->color == dst->color) {
        if (src->color == Scaler::ColorFormat::YUV_I420) {
          dst->stride[0] = src->width;
          dst->stride[1] = dst->stride[2] = src->width >> 1;
          memcpy(dst->data[0], src->data[0], src->stride[0] * src->height);
          memcpy(dst->data[1], src->data[1], dst->stride[1] * dst->height / 2);
          memcpy(dst->data[1], src->data[1], dst->stride[1] * dst->height / 2);
        } else if (src->color <= ColorFormat::YUV_NV12) {
          dst->stride[0] = dst->stride[1] = src->width;
          memcpy(dst->data[0], src->data[0], src->stride[0] * src->height);
          memcpy(dst->data[1], src->data[1], src->stride[1] * src->height);
        } else if (src->color <= ColorFormat::ARGB) {
          dst->stride[0] = src->stride[0];
          memcpy(dst->data[0], src->data[0], dst->stride[0] * src->height);
        } else {
          LOG(ERROR) << "scaler not support this color space";
        }
        return true;
      } else {
        src_crop = new Scaler::Buffer();
        src_crop->color = Scaler::ColorFormat::YUV_I420;
        src_crop->width = crop->w;
        src_crop->height = crop->h;
        src_crop->stride[0] = src_crop->width;
        src_crop->stride[1] = src_crop->stride[2] = src_crop->width >> 1;
        src_crop->data[0] = new uint8_t[src_crop->width * src_crop->height * 3 / 2];
        src_crop->data[1] = src_crop->data[0] + src_crop->width * src_crop->height;
        src_crop->data[2] = src_crop->data[1] + src_crop->width * src_crop->height / 4;
        Scaler::LibyuvToI420(src, src_crop, crop);
      }
    }
    

    if (width != dst->width || height != dst->height) {
      resize_buf = new Scaler::Buffer();
      memset(resize_buf, 0, sizeof(Scaler::Buffer));
      resize_buf->color= Scaler::ColorFormat::YUV_I420;
      resize_buf->width = dst->width;
      resize_buf->height = dst->height;
      resize_buf->stride[0] = resize_buf->width;
      resize_buf->stride[1] = resize_buf->stride[2] = resize_buf->width >> 1;
      resize_buf->data[0] = new uint8_t[resize_buf->width * resize_buf->height * 3 / 2];
      resize_buf->data[1] = resize_buf->data[0] + resize_buf->width * resize_buf->height;
      resize_buf->data[2] = resize_buf->data[1] + resize_buf->width * resize_buf->height / 4;

      if (src_crop == nullptr) {
        src_crop = new Scaler::Buffer();
        src_crop->color = Scaler::ColorFormat::YUV_I420;
        src_crop->width = src->width;
        src_crop->height = src->height;
        src_crop->stride[0] = src_crop->width;
        src_crop->stride[1] = src_crop->stride[2] = src_crop->width >> 1;
        src_crop->data[0] = new uint8_t[src_crop->width * src_crop->height * 3 / 2];
        src_crop->data[1] = src_crop->data[0] + src_crop->width * src_crop->height;
        src_crop->data[2] = src_crop->data[1] + src_crop->width * src_crop->height / 4;
        Scaler::LibyuvToI420(src, src_crop, nullptr); 
      } 

      libyuv::I420Scale(src_crop->data[0], src_crop->stride[0],
                        src_crop->data[1], src_crop->stride[1],
                        src_crop->data[2], src_crop->stride[2],
                        src_crop->width, src_crop->height,
                        resize_buf->data[0], resize_buf->stride[0],
                        resize_buf->data[1], resize_buf->stride[1],
                        resize_buf->data[2], resize_buf->stride[2],
                        resize_buf->width, resize_buf->height,
                        libyuv::FilterMode::kFilterNone);
    }

    if (dst->color != Scaler::ColorFormat::YUV_I420) {
      // i420 covert to dst->color, output to dst->data
      if (resize_buf != nullptr) {
        Scaler::LibyuvConvertFromI420(resize_buf, dst);
        if (src_crop != nullptr) {
          delete[] src_crop->data[0];
          src_crop->data[0] = nullptr;
          delete src_crop;
          src_crop = nullptr;
        }
        delete[] resize_buf->data[0];
        delete resize_buf;
        resize_buf->data[0] = resize_buf->data[1] = resize_buf->data[2] = nullptr;
        resize_buf = nullptr;
        return true;
      }  
      if (src_crop == nullptr) {
        src_crop = new Scaler::Buffer();
        src_crop->color = Scaler::ColorFormat::YUV_I420;
        src_crop->width = src->width;
        src_crop->height = src->height;
        src_crop->stride[0] = src_crop->width;
        src_crop->stride[1] = src_crop->stride[2] = src_crop->width >> 1;
        src_crop->data[0] = new uint8_t[src_crop->width * src_crop->height * 3 / 2];
        src_crop->data[1] = src_crop->data[0] + src_crop->width * src_crop->height;
        src_crop->data[2] = src_crop->data[1] + src_crop->width * src_crop->height / 4;
        Scaler::LibyuvToI420(src, src_crop, nullptr);
      }
      Scaler::LibyuvConvertFromI420(src_crop, dst);
      if (src_crop != nullptr) {
        delete[] src_crop->data[0];
        delete src_crop;
        src_crop->data[0] = src_crop->data[1] = src_crop->data[2] = nullptr;
        src_crop = nullptr;
      }
      return true;

    } else {
      if (resize_buf != nullptr) {
        memcpy(dst->data[0], resize_buf->data[0], dst->width * dst->height * 3 / 2);
      } else {
        Scaler::LibyuvToI420(src, dst, nullptr); 
      }
    }
    if (src_crop != nullptr) {
      delete[] src_crop->data[0];
      src_crop->data[0] = src_crop->data[1] = src_crop->data[2] = nullptr;
      delete src_crop;
      src_crop = nullptr;
    }
    if (resize_buf != nullptr) {
      delete[] resize_buf->data[0];
      resize_buf->data[0] = resize_buf->data[1] = resize_buf->data[2] = nullptr;
      delete resize_buf;
      resize_buf = nullptr;
    }
    return true;
}

轉換成I420

void Scaler::LibyuvToI420(const Buffer *src,
                          Buffer *dst,
                          const Rect *crop) {
  size_t frame_size = 0;
  if (src->color <= Scaler::ColorFormat::YUV_NV12) {
    frame_size = src->stride[0] * src->height * 3 / 2;
  } else if (src->color <= Scaler::ColorFormat::ARGB) {
    frame_size = src->stride[0] * src->height;
  }
  uint32_t fourcc = Scaler::GetFOURCC(src);
  if (crop != nullptr) {
    libyuv::ConvertToI420(src->data[0], frame_size,
                          dst->data[0], dst->stride[0],
                          dst->data[1], dst->stride[1],
                          dst->data[2], dst->stride[2],
                          crop->x, crop->y,
                          src->width, src->height,
                          crop->w, crop->h,
                          libyuv::RotationMode::kRotate0,
                          fourcc);

  } else {
    libyuv::ConvertToI420(src->data[0], frame_size,
                         dst->data[0], dst->stride[0],
                         dst->data[1], dst->stride[1],
                         dst->data[2], dst->stride[2],
                         0, 0,
                         src->width, src->height,
                         dst->width, dst->height,
                         libyuv::RotationMode::kRotate0,
                         fourcc);
  }

}

由I420轉成輸出格式

void Scaler::LibyuvConvertFromI420(const Buffer *src,
                                   Buffer *dst) {
  /*
  size_t frame_size = 0;
  if (src->color <= Scaler::ColorFormat::YUV_NV12) {
    frame_size = src->stride[0] * src->height * 3 / 2;
  } else if (src->color <= Scaler::ColorFormat::ARGB) {
    frame_size = src->stride[0] * src->height;
  }
  */
  uint32_t fourcc = Scaler::GetFOURCC(dst);
  libyuv::ConvertFromI420(src->data[0], src->stride[0],
                          src->data[1], src->stride[1],
                          src->data[2], src->stride[2],
                          dst->data[0],dst->stride[0],
                          dst->width, dst->height,
                          fourcc);
}

因為轉成i420中間格式,所以要求圖片長寬必須是偶數