mirror of
				https://github.com/pion/mediadevices.git
				synced 2025-11-01 04:12:45 +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
	 Atsushi Watanabe
					Atsushi Watanabe