diff --git a/pkg/avfoundation/AVFoundationBind/AVFoundationBind.h b/pkg/avfoundation/AVFoundationBind/AVFoundationBind.h index f302c346..d5aa6bb4 100644 --- a/pkg/avfoundation/AVFoundationBind/AVFoundationBind.h +++ b/pkg/avfoundation/AVFoundationBind/AVFoundationBind.h @@ -48,6 +48,8 @@ typedef enum AVBindFrameFormat { AVBindFrameFormatNV12, AVBindFrameFormatYUY2, 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 403a8403..9911a6fe 100644 --- a/pkg/avfoundation/AVFoundationBind/AVFoundationBind.m +++ b/pkg/avfoundation/AVFoundationBind/AVFoundationBind.m @@ -145,6 +145,12 @@ STATUS frameFormatToFourCC(AVBindFrameFormat format, FourCharCode *pFourCC) { case AVBindFrameFormatYUY2: *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; @@ -170,6 +176,12 @@ STATUS frameFormatFromFourCC(FourCharCode fourCC, AVBindFrameFormat *pFormat) { case kCVPixelFormatType_422YpCbCr8_yuvs: *pFormat = AVBindFrameFormatYUY2; 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 b42e6db8..1f6ed676 100644 --- a/pkg/avfoundation/avfoundation_darwin.go +++ b/pkg/avfoundation/avfoundation_darwin.go @@ -46,6 +46,10 @@ func frameFormatToAVBind(f frame.Format) (C.AVBindFrameFormat, bool) { return C.AVBindFrameFormatYUY2, 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 } @@ -63,6 +67,10 @@ func frameFormatFromAVBind(f C.AVBindFrameFormat) (frame.Format, bool) { return frame.FormatYUY2, 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 a1ef3f0d..4f8eb7ad 100644 --- a/pkg/frame/decode.go +++ b/pkg/frame/decode.go @@ -22,6 +22,8 @@ const ( // FormatRGBA https://www.fourcc.org/pixel-format/rgb-rgba/ FormatRGBA Format = "RGBA" + FormatARGB Format = "ARGB" + FormatBGRA Format = "BGRA" // FormatMJPEG https://www.fourcc.org/mjpg/ FormatMJPEG = "MJPEG" @@ -40,6 +42,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) + } + } + }) + } +}