github.com/aamcrae/webcam@v0.0.0-20210915060337-934acc13bdc3/frame/mjpeg.go (about)

     1  // Copyright 2019 Google LLC
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     https://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package frame
    16  
    17  import (
    18  	"bytes"
    19  	"fmt"
    20  	"image"
    21  	"image/color"
    22  	"image/jpeg"
    23  	"runtime"
    24  )
    25  
    26  type fMJPEG struct {
    27  	img     image.Image
    28  	release func()
    29  }
    30  
    31  const (
    32  	sectionFlag = 0xFF
    33  	soiMarker   = 0xd8
    34  	eoiMarker   = 0xd9
    35  	dhtMarker   = 0xc4
    36  	sosMarker   = 0xda
    37  )
    38  
    39  // Default Huffman tables.
    40  var default_dht []byte = []byte{
    41  	0xff, 0xc4, 0x01, 0xa2,
    42  
    43  	0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00,
    44  	0x00, 0x00, 0x00, 0x00, 0x00,
    45  	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
    46  
    47  	0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    48  	0x00, 0x00, 0x00, 0x00, 0x00,
    49  	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
    50  
    51  	0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04,
    52  	0x04, 0x00, 0x00, 0x01, 0x7d,
    53  	0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06,
    54  	0x13, 0x51, 0x61, 0x07,
    55  	0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1,
    56  	0x15, 0x52, 0xd1, 0xf0,
    57  	0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a,
    58  	0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
    59  	0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55,
    60  	0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
    61  	0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85,
    62  	0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
    63  	0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2,
    64  	0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
    65  	0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8,
    66  	0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
    67  	0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa,
    68  
    69  	0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04,
    70  	0x04, 0x00, 0x01, 0x02, 0x77,
    71  	0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41,
    72  	0x51, 0x07, 0x61, 0x71,
    73  	0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09,
    74  	0x23, 0x33, 0x52, 0xf0,
    75  	0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17,
    76  	0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
    77  	0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54,
    78  	0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
    79  	0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83,
    80  	0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
    81  	0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9,
    82  	0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
    83  	0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6,
    84  	0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
    85  	0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa,
    86  }
    87  
    88  // Register this framer for this format.
    89  func init() {
    90  	RegisterFramer("MJPG", newMJPGFramer)
    91  }
    92  
    93  func newMJPGFramer(w, h, stride, size int) func([]byte, func()) (Frame, error) {
    94  	return mjpegFramer
    95  }
    96  
    97  // Wrap a mjpeg block in a Frame so that it can be used as an image.
    98  // The standard jpeg decoding does not work if there are no Huffman tables,
    99  // so check the frame and add a default table if required.
   100  func mjpegFramer(f []byte, rel func()) (Frame, error) {
   101  	img, err := decodeMJPEG(f)
   102  	if err != nil {
   103  		if rel != nil {
   104  			rel()
   105  		}
   106  		return nil, err
   107  	}
   108  	fr := &fMJPEG{img: img, release: rel}
   109  	runtime.SetFinalizer(fr, func(obj Frame) {
   110  		obj.Release()
   111  	})
   112  	return fr, nil
   113  }
   114  
   115  // decodeMJPEG decodes the frame into an image.
   116  func decodeMJPEG(f []byte) (image.Image, error) {
   117  	sect, err := findConfig(f)
   118  	if err != nil {
   119  		return nil, err
   120  	}
   121  	_, ok := sect[dhtMarker]
   122  	var buf *bytes.Buffer
   123  	if !ok {
   124  		s, ok := sect[sosMarker]
   125  		if !ok {
   126  			return nil, fmt.Errorf("no scan data in image")
   127  		}
   128  		// Insert the default Huffman table before the start
   129  		// of the scan data.
   130  		ins := s[0]
   131  		buf = new(bytes.Buffer)
   132  		buf.Write(f[:ins])
   133  		buf.Write(default_dht)
   134  		buf.Write(f[ins:])
   135  	} else {
   136  		buf = bytes.NewBuffer(f)
   137  	}
   138  	return jpeg.Decode(buf)
   139  }
   140  
   141  func (f *fMJPEG) ColorModel() color.Model {
   142  	return f.img.ColorModel()
   143  }
   144  
   145  func (f *fMJPEG) Bounds() image.Rectangle {
   146  	return f.img.Bounds()
   147  }
   148  
   149  func (f *fMJPEG) At(x, y int) color.Color {
   150  	return f.img.At(x, y)
   151  }
   152  
   153  // Done with frame, release back to camera (if required).
   154  func (f *fMJPEG) Release() {
   155  	if f.release != nil {
   156  		f.release()
   157  		// Make sure it only gets called once.
   158  		f.release = nil
   159  	}
   160  }
   161  
   162  // findConfig returns a map of the different config markers and their location.
   163  func findConfig(f []byte) (map[byte][]int, error) {
   164  	m := make(map[byte][]int)
   165  	for l := 0; l < len(f)-1; {
   166  		if f[l] != sectionFlag {
   167  			return nil, fmt.Errorf("No section marker at location %d", l)
   168  		}
   169  		l++
   170  		marker := f[l]
   171  		m[marker] = append(m[marker], l-1)
   172  		l++
   173  		if marker == soiMarker || marker == eoiMarker {
   174  			continue
   175  		}
   176  		if marker == sosMarker {
   177  			break
   178  		}
   179  		// next 2 bytes are length of the section (big-endian).
   180  		if l >= len(f)-2 {
   181  			return nil, fmt.Errorf("unexpected EOF at location %d", l)
   182  		}
   183  		l += (int(f[l]) << 8) + int(f[l+1])
   184  	}
   185  	return m, nil
   186  }