libyuv實現通用的cvt,resize,crop功能
阿新 • • 發佈:2020-10-09
我實現的思路是所有的格式先轉成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);
}