1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 // Routines for encoding and decoding a small number of bits into an image 6 // in a way that is decodable even after scaling/encoding/cropping. 7 // 8 // The encoding is very simple: 9 // 10 // #### #### ######## #### #### #### 11 // #### #### ######## #### #### #### 12 // #### #### ######## #### #### #### 13 // #### #### ######## #### #### #### 14 // 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 // <-----start----><--one-bit-><-zero bit-><----stop----> 16 // 17 // We use a basic unit, depicted here as four characters wide. 18 // We start with 1u black 1u white 1u black 1u white. (1-4 above) 19 // From there on, a "one" bit is encoded as 2u black and 1u white, 20 // and a zero bit is encoded as 1u black and 2u white. After 21 // all the bits we end the pattern with the same pattern as the 22 // start of the pattern. 23 24 #include <deque> 25 #include <vector> 26 27 #include "base/logging.h" 28 #include "media/base/video_frame.h" 29 #include "media/cast/test/utility/barcode.h" 30 31 namespace media { 32 namespace cast { 33 namespace test { 34 35 const int kBlackThreshold = 256 * 2 / 3; 36 const int kWhiteThreshold = 256 / 3; 37 38 bool EncodeBarcode(const std::vector<bool>& bits, 39 scoped_refptr<VideoFrame> output_frame) { 40 DCHECK(output_frame->format() == VideoFrame::YV12 || 41 output_frame->format() == VideoFrame::YV16 || 42 output_frame->format() == VideoFrame::I420 || 43 output_frame->format() == VideoFrame::YV12J); 44 int row_bytes = output_frame->row_bytes(VideoFrame::kYPlane); 45 std::vector<unsigned char> bytes(row_bytes); 46 for (int i = 0; i < row_bytes; i++) { 47 bytes[i] = 255; 48 } 49 size_t units = bits.size() * 3 + 7; // White or black bar where size matters. 50 // We only use 60% of the image to make sure it works even if 51 // the image gets cropped. 52 size_t unit_size = row_bytes * 6 / 10 / units; 53 if (unit_size < 1) return false; 54 size_t bytes_required = unit_size * units; 55 size_t padding = (row_bytes - bytes_required) / 2; 56 unsigned char *pos = &bytes[padding]; 57 // Two leading black bars. 58 memset(pos, 0, unit_size); 59 pos += unit_size * 2; 60 memset(pos, 0, unit_size); 61 pos += unit_size * 2; 62 for (size_t bit = 0; bit < bits.size(); bit++) { 63 memset(pos, 0, bits[bit] ? unit_size * 2: unit_size); 64 pos += unit_size * 3; 65 } 66 memset(pos, 0, unit_size); 67 pos += unit_size * 2; 68 memset(pos, 0, unit_size); 69 pos += unit_size; 70 DCHECK_LE(pos - &bytes.front(), row_bytes); 71 72 // Now replicate this one row into all rows in kYPlane. 73 for (int row = 0; row < output_frame->rows(VideoFrame::kYPlane); row++) { 74 memcpy(output_frame->data(VideoFrame::kYPlane) + 75 output_frame->stride(VideoFrame::kYPlane) * row, 76 &bytes.front(), 77 row_bytes); 78 } 79 return true; 80 } 81 82 namespace { 83 bool DecodeBarCodeRows(const scoped_refptr<VideoFrame>& frame, 84 std::vector<bool>* output, 85 int min_row, 86 int max_row) { 87 // Do a basic run-length encoding 88 std::deque<int> runs; 89 bool is_black = true; 90 int length = 0; 91 for (int pos = 0; pos < frame->row_bytes(VideoFrame::kYPlane); pos++) { 92 float value = 0.0; 93 for (int row = min_row; row < max_row; row++) { 94 value += frame->data(VideoFrame::kYPlane)[ 95 frame->stride(VideoFrame::kYPlane) * row + pos]; 96 } 97 value /= max_row - min_row; 98 if (is_black ? value > kBlackThreshold : value < kWhiteThreshold) { 99 is_black = !is_black; 100 runs.push_back(length); 101 length = 1; 102 } else { 103 length++; 104 } 105 } 106 runs.push_back(length); 107 108 // Try decoding starting at each white-black transition. 109 while (runs.size() >= output->size() * 2 + 7) { 110 std::deque<int>::const_iterator i = runs.begin(); 111 double unit_size = (i[1] + i[2] + i[3] + i[4]) / 4; 112 bool valid = true; 113 if (i[0] > unit_size * 2 || i[0] < unit_size / 2) valid = false; 114 if (i[1] > unit_size * 2 || i[1] < unit_size / 2) valid = false; 115 if (i[2] > unit_size * 2 || i[2] < unit_size / 2) valid = false; 116 if (i[3] > unit_size * 2 || i[3] < unit_size / 2) valid = false; 117 i += 4; 118 for (size_t bit = 0; valid && bit < output->size(); bit++) { 119 if (i[0] > unit_size / 2 && i[0] <= unit_size * 1.5 && 120 i[1] > unit_size * 1.5 && i[1] <= unit_size * 3) { 121 (*output)[bit] = false; 122 } else if (i[1] > unit_size / 2 && i[1] <= unit_size * 1.5 && 123 i[0] > unit_size * 1.5 && i[0] <= unit_size * 3) { 124 (*output)[bit] = true; 125 } else { 126 // Not a valid code 127 valid = false; 128 } 129 i += 2; 130 } 131 if (i[0] > unit_size * 2 || i[0] < unit_size / 2) valid = false; 132 if (i[1] > unit_size * 2 || i[1] < unit_size / 2) valid = false; 133 if (i[2] > unit_size * 2 || i[2] < unit_size / 2) valid = false; 134 i += 3; 135 DCHECK(i <= runs.end()); 136 if (valid) { 137 // Decoding successful, return true 138 return true; 139 } 140 runs.pop_front(); 141 runs.pop_front(); 142 } 143 return false; 144 } 145 146 } // namespace 147 148 // Note that "output" is assumed to be the right size already. This 149 // could be inferred from the data, but the decoding is more robust 150 // if we can assume that we know how many bits we want. 151 bool DecodeBarcode(const scoped_refptr<VideoFrame>& frame, 152 std::vector<bool>* output) { 153 DCHECK(frame->format() == VideoFrame::YV12 || 154 frame->format() == VideoFrame::YV16 || 155 frame->format() == VideoFrame::I420 || 156 frame->format() == VideoFrame::YV12J); 157 int rows = frame->rows(VideoFrame::kYPlane); 158 // Middle 10 lines 159 if (DecodeBarCodeRows(frame, 160 output, 161 std::max(0, rows / 2 - 5), 162 std::min(rows, rows / 2 + 5))) { 163 return true; 164 } 165 166 // Top 5 lines 167 if (DecodeBarCodeRows(frame, output, 0, std::min(5, rows))) { 168 return true; 169 } 170 171 return false; 172 } 173 174 } // namespace test 175 } // namespace cast 176 } // namespace media 177