diff --git a/pkg/avfoundation/AVFoundationBind/AVFoundationBind.h b/pkg/avfoundation/AVFoundationBind/AVFoundationBind.h index ab0b1d73..a34049f2 100644 --- a/pkg/avfoundation/AVFoundationBind/AVFoundationBind.h +++ b/pkg/avfoundation/AVFoundationBind/AVFoundationBind.h @@ -49,6 +49,8 @@ typedef enum AVBindFrameFormat { AVBindFrameFormatNV12, AVBindFrameFormatYUYV, AVBindFrameFormatUYVY, + AVBindFrameFormatBGRA, + AVBindFrameFormatARGB, } AVBindFrameFormat; typedef void (*AVBindDataCallback)(void *userData, void *buf, int len); diff --git a/pkg/avfoundation/AVFoundationBind/AVFoundationBind.m b/pkg/avfoundation/AVFoundationBind/AVFoundationBind.m index c4f9cd90..5aac06aa 100644 --- a/pkg/avfoundation/AVFoundationBind/AVFoundationBind.m +++ b/pkg/avfoundation/AVFoundationBind/AVFoundationBind.m @@ -179,6 +179,12 @@ STATUS frameFormatToFourCC(AVBindFrameFormat format, FourCharCode *pFourCC) { case AVBindFrameFormatYUYV: *pFourCC = kCVPixelFormatType_422YpCbCr8_yuvs; break; + case AVBindFrameFormatBGRA: + *pFourCC = kCVPixelFormatType_32ARGB; + break; + case AVBindFrameFormatARGB: + *pFourCC = kCVPixelFormatType_32BGRA; + break; // TODO: Add the rest of frame formats default: retStatus = STATUS_UNSUPPORTED_FRAME_FORMAT; @@ -202,6 +208,12 @@ STATUS frameFormatFromFourCC(FourCharCode fourCC, AVBindFrameFormat *pFormat) { case kCVPixelFormatType_422YpCbCr8_yuvs: *pFormat = AVBindFrameFormatYUYV; break; + case kCVPixelFormatType_32ARGB: + *pFormat = AVBindFrameFormatBGRA; + break; + case kCVPixelFormatType_32BGRA: + *pFormat = AVBindFrameFormatARGB; + break; // TODO: Add the rest of frame formats default: retStatus = STATUS_UNSUPPORTED_FRAME_FORMAT; diff --git a/pkg/avfoundation/avfoundation_darwin.go b/pkg/avfoundation/avfoundation_darwin.go index a7e10317..a5853957 100644 --- a/pkg/avfoundation/avfoundation_darwin.go +++ b/pkg/avfoundation/avfoundation_darwin.go @@ -49,6 +49,10 @@ func frameFormatToAVBind(f frame.Format) (C.AVBindFrameFormat, bool) { return C.AVBindFrameFormatYUYV, true case frame.FormatUYVY: return C.AVBindFrameFormatUYVY, true + case frame.FormatBGRA: + return C.AVBindFrameFormatBGRA, true + case frame.FormatARGB: + return C.AVBindFrameFormatARGB, true default: return 0, false } @@ -66,6 +70,10 @@ func frameFormatFromAVBind(f C.AVBindFrameFormat) (frame.Format, bool) { return frame.FormatYUYV, true case C.AVBindFrameFormatUYVY: return frame.FormatUYVY, true + case C.AVBindFrameFormatBGRA: + return frame.FormatBGRA, true + case C.AVBindFrameFormatARGB: + return frame.FormatARGB, true default: return "", false } diff --git a/pkg/frame/decode.go b/pkg/frame/decode.go index 659a2bfb..884137f9 100644 --- a/pkg/frame/decode.go +++ b/pkg/frame/decode.go @@ -25,6 +25,8 @@ const ( // FormatRGBA https://www.kernel.org/doc/html/v5.9/userspace-api/media/v4l/pixfmt-rgb.html FormatRGBA Format = "RGBA" + FormatARGB Format = "ARGB" + FormatBGRA Format = "BGRA" // FormatMJPEG https://wiki.videolan.org/MJPEG FormatMJPEG = "MJPEG" @@ -42,6 +44,8 @@ var decoderMap = map[Format]decoderFunc{ FormatUYVY: decodeUYVY, FormatMJPEG: decodeMJPEG, FormatZ16: decodeZ16, + FormatARGB: decodeARGB, + FormatBGRA: decodeBGRA, } func NewDecoder(f Format) (Decoder, error) { diff --git a/pkg/frame/rgb.go b/pkg/frame/rgb.go new file mode 100644 index 00000000..48824536 --- /dev/null +++ b/pkg/frame/rgb.go @@ -0,0 +1,44 @@ +package frame + +import ( + "fmt" + "image" + "math/bits" + "unsafe" +) + +func decodeARGB(frame []byte, width, height int) (image.Image, func(), error) { + size := 4 * width * height + if size > len(frame) { + return nil, func() {}, fmt.Errorf("frame length (%d) less than expected (%d)", len(frame), size) + } + r := image.Rect(0, 0, width, height) + for i := 0; i < size; i += 4 { + *(*uint32)(unsafe.Pointer(&frame[i])) = func(v uint32) uint32 { + return (v & 0xFF00FF00) | (v&0xFF)<<16 | (v&0xFF0000)>>16 + }(*(*uint32)(unsafe.Pointer(&frame[i]))) + //frame[i], frame[i+2] = frame[i+2], frame[i] + } + return &image.RGBA{ + Pix: frame[:size:size], + Stride: 4 * r.Dx(), + Rect: r, + }, func() {}, nil +} + +func decodeBGRA(frame []byte, width, height int) (image.Image, func(), error) { + size := 4 * width * height + if size > len(frame) { + return nil, func() {}, fmt.Errorf("frame length (%d) less than expected (%d)", len(frame), size) + } + r := image.Rect(0, 0, width, height) + for i := 0; i < size; i += 4 { + *(*uint32)(unsafe.Pointer(&frame[i])) = bits.RotateLeft32(*(*uint32)(unsafe.Pointer(&frame[i])), -8) + //frame[i], frame[i+1], frame[i+2], frame[i+3] = frame[i+1], frame[i+2], frame[i+3], frame[i] + } + return &image.RGBA{ + Pix: frame[:size:size], + Stride: 4 * r.Dx(), + Rect: r, + }, func() {}, nil +} diff --git a/pkg/frame/rgb_test.go b/pkg/frame/rgb_test.go new file mode 100644 index 00000000..ca8676d4 --- /dev/null +++ b/pkg/frame/rgb_test.go @@ -0,0 +1,48 @@ +package frame + +import ( + "fmt" + "testing" +) + +func BenchmarkDecodeBGRA(b *testing.B) { + sizes := []struct { + width, height int + }{ + {640, 480}, + {1920, 1080}, + } + for _, sz := range sizes { + sz := sz + b.Run(fmt.Sprintf("%dx%d", sz.width, sz.height), func(b *testing.B) { + input := make([]byte, sz.width*sz.height*4) + for i := 0; i < b.N; i++ { + _, _, err := decodeBGRA(input, sz.width, sz.height) + if err != nil { + b.Fatal(err) + } + } + }) + } +} + +func BenchmarkDecodeARGB(b *testing.B) { + sizes := []struct { + width, height int + }{ + {640, 480}, + {1920, 1080}, + } + for _, sz := range sizes { + sz := sz + b.Run(fmt.Sprintf("%dx%d", sz.width, sz.height), func(b *testing.B) { + input := make([]byte, sz.width*sz.height*4) + for i := 0; i < b.N; i++ { + _, _, err := decodeARGB(input, sz.width, sz.height) + if err != nil { + b.Fatal(err) + } + } + }) + } +}