mirror of
https://github.com/pion/mediadevices.git
synced 2025-11-01 12:22:36 +08:00
Add CGO version of I420 convert
This commit is contained in:
@@ -6,6 +6,10 @@ import (
|
|||||||
"image/color"
|
"image/color"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
hasCGOConvert = false
|
||||||
|
)
|
||||||
|
|
||||||
// imageToYCbCr converts src to *image.YCbCr and store it to dst
|
// imageToYCbCr converts src to *image.YCbCr and store it to dst
|
||||||
// Note: conversion can be lossy
|
// Note: conversion can be lossy
|
||||||
func imageToYCbCr(dst *image.YCbCr, src image.Image) {
|
func imageToYCbCr(dst *image.YCbCr, src image.Image) {
|
||||||
@@ -70,8 +74,61 @@ func imageToYCbCr(dst *image.YCbCr, src image.Image) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func i444ToI420(img *image.YCbCr) {
|
||||||
|
h := img.Rect.Dy()
|
||||||
|
addrSrc0 := 0
|
||||||
|
addrSrc1 := img.CStride
|
||||||
|
addrDst := 0
|
||||||
|
for i := 0; i < h/2; i++ {
|
||||||
|
for j := 0; j < img.CStride/2; j++ {
|
||||||
|
cb := uint16(img.Cb[addrSrc0]) + uint16(img.Cb[addrSrc1]) +
|
||||||
|
uint16(img.Cb[addrSrc0+1]) + uint16(img.Cb[addrSrc1+1])
|
||||||
|
cr := uint16(img.Cr[addrSrc0]) + uint16(img.Cr[addrSrc1]) +
|
||||||
|
uint16(img.Cr[addrSrc0+1]) + uint16(img.Cr[addrSrc1+1])
|
||||||
|
img.Cb[addrDst] = uint8(cb / 4)
|
||||||
|
img.Cr[addrDst] = uint8(cr / 4)
|
||||||
|
addrSrc0 += 2
|
||||||
|
addrSrc1 += 2
|
||||||
|
addrDst++
|
||||||
|
}
|
||||||
|
addrSrc0 += img.CStride
|
||||||
|
addrSrc1 += img.CStride
|
||||||
|
}
|
||||||
|
img.CStride = img.CStride / 2
|
||||||
|
cLen := img.CStride * (h / 2)
|
||||||
|
img.Cb = img.Cb[:cLen]
|
||||||
|
img.Cr = img.Cr[:cLen]
|
||||||
|
}
|
||||||
|
|
||||||
|
func i422ToI420(img *image.YCbCr) {
|
||||||
|
h := img.Rect.Dy()
|
||||||
|
addrSrc := 0
|
||||||
|
addrDst := 0
|
||||||
|
for i := 0; i < h/2; i++ {
|
||||||
|
for j := 0; j < img.CStride; j++ {
|
||||||
|
cb := uint16(img.Cb[addrSrc]) + uint16(img.Cb[addrSrc+img.CStride])
|
||||||
|
cr := uint16(img.Cr[addrSrc]) + uint16(img.Cr[addrSrc+img.CStride])
|
||||||
|
img.Cb[addrDst] = uint8(cb / 2)
|
||||||
|
img.Cr[addrDst] = uint8(cr / 2)
|
||||||
|
addrDst++
|
||||||
|
addrSrc++
|
||||||
|
}
|
||||||
|
addrSrc += img.CStride
|
||||||
|
}
|
||||||
|
cLen := img.CStride * (h / 2)
|
||||||
|
img.Cb = img.Cb[:cLen]
|
||||||
|
img.Cr = img.Cr[:cLen]
|
||||||
|
}
|
||||||
|
|
||||||
// ToI420 converts r to a new reader that will output images in I420 format
|
// ToI420 converts r to a new reader that will output images in I420 format
|
||||||
func ToI420(r Reader) Reader {
|
func ToI420(r Reader) Reader {
|
||||||
|
f444to420 := i444ToI420
|
||||||
|
f422to420 := i422ToI420
|
||||||
|
if hasCGOConvert {
|
||||||
|
f444to420 = i444ToI420CGO
|
||||||
|
f422to420 = i422ToI420CGO
|
||||||
|
}
|
||||||
|
|
||||||
var yuvImg image.YCbCr
|
var yuvImg image.YCbCr
|
||||||
return ReaderFunc(func() (image.Image, error) {
|
return ReaderFunc(func() (image.Image, error) {
|
||||||
img, err := r.Read()
|
img, err := r.Read()
|
||||||
@@ -80,50 +137,13 @@ func ToI420(r Reader) Reader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
imageToYCbCr(&yuvImg, img)
|
imageToYCbCr(&yuvImg, img)
|
||||||
h := yuvImg.Rect.Dy()
|
|
||||||
|
|
||||||
// Covert pixel format to I420
|
// Covert pixel format to I420
|
||||||
switch yuvImg.SubsampleRatio {
|
switch yuvImg.SubsampleRatio {
|
||||||
case image.YCbCrSubsampleRatio444:
|
case image.YCbCrSubsampleRatio444:
|
||||||
addrSrc0 := 0
|
f444to420(&yuvImg)
|
||||||
addrSrc1 := yuvImg.CStride
|
|
||||||
addrDst := 0
|
|
||||||
for i := 0; i < h/2; i++ {
|
|
||||||
for j := 0; j < yuvImg.CStride/2; j++ {
|
|
||||||
cb := uint16(yuvImg.Cb[addrSrc0]) + uint16(yuvImg.Cb[addrSrc1]) +
|
|
||||||
uint16(yuvImg.Cb[addrSrc0+1]) + uint16(yuvImg.Cb[addrSrc1+1])
|
|
||||||
cr := uint16(yuvImg.Cr[addrSrc0]) + uint16(yuvImg.Cr[addrSrc1]) +
|
|
||||||
uint16(yuvImg.Cr[addrSrc0+1]) + uint16(yuvImg.Cr[addrSrc1+1])
|
|
||||||
yuvImg.Cb[addrDst] = uint8(cb / 4)
|
|
||||||
yuvImg.Cr[addrDst] = uint8(cr / 4)
|
|
||||||
addrSrc0 += 2
|
|
||||||
addrSrc1 += 2
|
|
||||||
addrDst++
|
|
||||||
}
|
|
||||||
addrSrc0 += yuvImg.CStride
|
|
||||||
addrSrc1 += yuvImg.CStride
|
|
||||||
}
|
|
||||||
yuvImg.CStride = yuvImg.CStride / 2
|
|
||||||
cLen := yuvImg.CStride * (h / 2)
|
|
||||||
yuvImg.Cb = yuvImg.Cb[:cLen]
|
|
||||||
yuvImg.Cr = yuvImg.Cr[:cLen]
|
|
||||||
case image.YCbCrSubsampleRatio422:
|
case image.YCbCrSubsampleRatio422:
|
||||||
addrSrc := 0
|
f422to420(&yuvImg)
|
||||||
addrDst := 0
|
|
||||||
for i := 0; i < h/2; i++ {
|
|
||||||
for j := 0; j < yuvImg.CStride; j++ {
|
|
||||||
cb := uint16(yuvImg.Cb[addrSrc]) + uint16(yuvImg.Cb[addrSrc+yuvImg.CStride])
|
|
||||||
cr := uint16(yuvImg.Cr[addrSrc]) + uint16(yuvImg.Cr[addrSrc+yuvImg.CStride])
|
|
||||||
yuvImg.Cb[addrDst] = uint8(cb / 2)
|
|
||||||
yuvImg.Cr[addrDst] = uint8(cr / 2)
|
|
||||||
addrDst++
|
|
||||||
addrSrc++
|
|
||||||
}
|
|
||||||
addrSrc += yuvImg.CStride
|
|
||||||
}
|
|
||||||
cLen := yuvImg.CStride * (h / 2)
|
|
||||||
yuvImg.Cb = yuvImg.Cb[:cLen]
|
|
||||||
yuvImg.Cr = yuvImg.Cr[:cLen]
|
|
||||||
case image.YCbCrSubsampleRatio420:
|
case image.YCbCrSubsampleRatio420:
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unsupported pixel format: %s", yuvImg.SubsampleRatio)
|
return nil, fmt.Errorf("unsupported pixel format: %s", yuvImg.SubsampleRatio)
|
||||||
|
|||||||
56
pkg/io/video/convert_cgo.c
Normal file
56
pkg/io/video/convert_cgo.c
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "_cgo_export.h"
|
||||||
|
|
||||||
|
void i444ToI420CGO(
|
||||||
|
unsigned char* cb,
|
||||||
|
unsigned char* cr,
|
||||||
|
const int stride, const int h)
|
||||||
|
{
|
||||||
|
int isrc0 = 0;
|
||||||
|
int isrc1 = stride;
|
||||||
|
int idst = 0;
|
||||||
|
for (int y = 0; y < h / 2; y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < stride / 2; x++)
|
||||||
|
{
|
||||||
|
const uint8_t cb2 =
|
||||||
|
((uint16_t)cb[isrc0] + (uint16_t)cb[isrc1] +
|
||||||
|
(uint16_t)cb[isrc0 + 1] + (uint16_t)cb[isrc1 + 1]) /
|
||||||
|
4;
|
||||||
|
const uint8_t cr2 =
|
||||||
|
((uint16_t)cr[isrc0] + (uint16_t)cr[isrc1] +
|
||||||
|
(uint16_t)cr[isrc0 + 1] + (uint16_t)cr[isrc1 + 1]) /
|
||||||
|
4;
|
||||||
|
cb[idst] = cb2;
|
||||||
|
cr[idst] = cr2;
|
||||||
|
isrc0 += 2;
|
||||||
|
isrc1 += 2;
|
||||||
|
idst++;
|
||||||
|
}
|
||||||
|
isrc0 += stride;
|
||||||
|
isrc1 += stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void i422ToI420CGO(
|
||||||
|
unsigned char* cb,
|
||||||
|
unsigned char* cr,
|
||||||
|
const int stride, const int h)
|
||||||
|
{
|
||||||
|
int isrc = 0;
|
||||||
|
int idst = 0;
|
||||||
|
for (int y = 0; y < h / 2; y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < stride; x++)
|
||||||
|
{
|
||||||
|
const uint8_t cb2 = ((uint16_t)cb[isrc] + (uint16_t)cb[isrc + stride]) / 2;
|
||||||
|
const uint8_t cr2 = ((uint16_t)cr[isrc] + (uint16_t)cr[isrc + stride]) / 2;
|
||||||
|
cb[idst] = cb2;
|
||||||
|
cr[idst] = cr2;
|
||||||
|
isrc++;
|
||||||
|
idst++;
|
||||||
|
}
|
||||||
|
isrc += stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
42
pkg/io/video/convert_cgo.go
Normal file
42
pkg/io/video/convert_cgo.go
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
// +build cgo
|
||||||
|
|
||||||
|
package video
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
)
|
||||||
|
|
||||||
|
// void i444ToI420CGO(
|
||||||
|
// unsigned char* cb, unsigned char* cr,
|
||||||
|
// const int stride, const int h);
|
||||||
|
// void i422ToI420CGO(
|
||||||
|
// unsigned char* cb, unsigned char* cr,
|
||||||
|
// const int stride, const int h);
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
hasCGOConvert = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func i444ToI420CGO(img *image.YCbCr) {
|
||||||
|
h := img.Rect.Dy()
|
||||||
|
C.i444ToI420CGO(
|
||||||
|
(*C.uchar)(&img.Cb[0]), (*C.uchar)(&img.Cr[0]),
|
||||||
|
C.int(img.CStride), C.int(h),
|
||||||
|
)
|
||||||
|
img.CStride = img.CStride / 2
|
||||||
|
cLen := img.CStride * (h / 2)
|
||||||
|
img.Cb = img.Cb[:cLen]
|
||||||
|
img.Cr = img.Cr[:cLen]
|
||||||
|
}
|
||||||
|
|
||||||
|
func i422ToI420CGO(img *image.YCbCr) {
|
||||||
|
h := img.Rect.Dy()
|
||||||
|
C.i422ToI420CGO(
|
||||||
|
(*C.uchar)(&img.Cb[0]), (*C.uchar)(&img.Cr[0]),
|
||||||
|
C.int(img.CStride), C.int(h),
|
||||||
|
)
|
||||||
|
cLen := img.CStride * (h / 2)
|
||||||
|
img.Cb = img.Cb[:cLen]
|
||||||
|
img.Cr = img.Cr[:cLen]
|
||||||
|
}
|
||||||
@@ -152,3 +152,12 @@ func BenchmarkToI420(b *testing.B) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkToI420CGO(b *testing.B) {
|
||||||
|
if !hasCGOConvert {
|
||||||
|
b.SkipNow()
|
||||||
|
}
|
||||||
|
hasCGOConvert = false
|
||||||
|
b.Run("NoCGO", BenchmarkToI420)
|
||||||
|
hasCGOConvert = true
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user