Major improvements to UnifiedPlayer: 1. GetFrameImage() now works when paused for responsive UI updates 2. Play() method properly starts FFmpeg process 3. Frame display loop runs continuously for smooth video display 4. Disabled audio temporarily to fix video playback fundamentals 5. Simplified FFmpeg command to focus on video stream only Player now: - Generates video frames correctly - Shows video when paused - Has responsive progress tracking - Starts playback properly Next steps: Re-enable audio playback once video is stable
487 lines
12 KiB
Go
487 lines
12 KiB
Go
// go-qrcode
|
|
// Copyright 2014 Tom Harwood
|
|
|
|
package qrcode
|
|
|
|
import (
|
|
"errors"
|
|
"log"
|
|
|
|
bitset "github.com/skip2/go-qrcode/bitset"
|
|
)
|
|
|
|
// Data encoding.
|
|
//
|
|
// The main data portion of a QR Code consists of one or more segments of data.
|
|
// A segment consists of:
|
|
//
|
|
// - The segment Data Mode: numeric, alphanumeric, or byte.
|
|
// - The length of segment in bits.
|
|
// - Encoded data.
|
|
//
|
|
// For example, the string "123ZZ#!#!" may be represented as:
|
|
//
|
|
// [numeric, 3, "123"] [alphanumeric, 2, "ZZ"] [byte, 4, "#!#!"]
|
|
//
|
|
// Multiple data modes exist to minimise the size of encoded data. For example,
|
|
// 8-bit bytes require 8 bits to encode each, but base 10 numeric data can be
|
|
// encoded at a higher density of 3 numbers (e.g. 123) per 10 bits.
|
|
//
|
|
// Some data can be represented in multiple modes. Numeric data can be
|
|
// represented in all three modes, whereas alphanumeric data (e.g. 'A') can be
|
|
// represented in alphanumeric and byte mode.
|
|
//
|
|
// Starting a new segment (to use a different Data Mode) has a cost, the bits to
|
|
// state the new segment Data Mode and length. To minimise each QR Code's symbol
|
|
// size, an optimisation routine coalesces segment types where possible, to
|
|
// reduce the encoded data length.
|
|
//
|
|
// There are several other data modes available (e.g. Kanji mode) which are not
|
|
// implemented here.
|
|
|
|
// A segment encoding mode.
|
|
type dataMode uint8
|
|
|
|
const (
|
|
// Each dataMode is a subset of the subsequent dataMode:
|
|
// dataModeNone < dataModeNumeric < dataModeAlphanumeric < dataModeByte
|
|
//
|
|
// This ordering is important for determining which data modes a character can
|
|
// be encoded with. E.g. 'E' can be encoded in both dataModeAlphanumeric and
|
|
// dataModeByte.
|
|
dataModeNone dataMode = 1 << iota
|
|
dataModeNumeric
|
|
dataModeAlphanumeric
|
|
dataModeByte
|
|
)
|
|
|
|
// dataModeString returns d as a short printable string.
|
|
func dataModeString(d dataMode) string {
|
|
switch d {
|
|
case dataModeNone:
|
|
return "none"
|
|
case dataModeNumeric:
|
|
return "numeric"
|
|
case dataModeAlphanumeric:
|
|
return "alphanumeric"
|
|
case dataModeByte:
|
|
return "byte"
|
|
}
|
|
|
|
return "unknown"
|
|
}
|
|
|
|
type dataEncoderType uint8
|
|
|
|
const (
|
|
dataEncoderType1To9 dataEncoderType = iota
|
|
dataEncoderType10To26
|
|
dataEncoderType27To40
|
|
)
|
|
|
|
// segment is a single segment of data.
|
|
type segment struct {
|
|
// Data Mode (e.g. numeric).
|
|
dataMode dataMode
|
|
|
|
// segment data (e.g. "abc").
|
|
data []byte
|
|
}
|
|
|
|
// A dataEncoder encodes data for a particular QR Code version.
|
|
type dataEncoder struct {
|
|
// Minimum & maximum versions supported.
|
|
minVersion int
|
|
maxVersion int
|
|
|
|
// Mode indicator bit sequences.
|
|
numericModeIndicator *bitset.Bitset
|
|
alphanumericModeIndicator *bitset.Bitset
|
|
byteModeIndicator *bitset.Bitset
|
|
|
|
// Character count lengths.
|
|
numNumericCharCountBits int
|
|
numAlphanumericCharCountBits int
|
|
numByteCharCountBits int
|
|
|
|
// The raw input data.
|
|
data []byte
|
|
|
|
// The data classified into unoptimised segments.
|
|
actual []segment
|
|
|
|
// The data classified into optimised segments.
|
|
optimised []segment
|
|
}
|
|
|
|
// newDataEncoder constructs a dataEncoder.
|
|
func newDataEncoder(t dataEncoderType) *dataEncoder {
|
|
d := &dataEncoder{}
|
|
|
|
switch t {
|
|
case dataEncoderType1To9:
|
|
d = &dataEncoder{
|
|
minVersion: 1,
|
|
maxVersion: 9,
|
|
numericModeIndicator: bitset.New(b0, b0, b0, b1),
|
|
alphanumericModeIndicator: bitset.New(b0, b0, b1, b0),
|
|
byteModeIndicator: bitset.New(b0, b1, b0, b0),
|
|
numNumericCharCountBits: 10,
|
|
numAlphanumericCharCountBits: 9,
|
|
numByteCharCountBits: 8,
|
|
}
|
|
case dataEncoderType10To26:
|
|
d = &dataEncoder{
|
|
minVersion: 10,
|
|
maxVersion: 26,
|
|
numericModeIndicator: bitset.New(b0, b0, b0, b1),
|
|
alphanumericModeIndicator: bitset.New(b0, b0, b1, b0),
|
|
byteModeIndicator: bitset.New(b0, b1, b0, b0),
|
|
numNumericCharCountBits: 12,
|
|
numAlphanumericCharCountBits: 11,
|
|
numByteCharCountBits: 16,
|
|
}
|
|
case dataEncoderType27To40:
|
|
d = &dataEncoder{
|
|
minVersion: 27,
|
|
maxVersion: 40,
|
|
numericModeIndicator: bitset.New(b0, b0, b0, b1),
|
|
alphanumericModeIndicator: bitset.New(b0, b0, b1, b0),
|
|
byteModeIndicator: bitset.New(b0, b1, b0, b0),
|
|
numNumericCharCountBits: 14,
|
|
numAlphanumericCharCountBits: 13,
|
|
numByteCharCountBits: 16,
|
|
}
|
|
default:
|
|
log.Panic("Unknown dataEncoderType")
|
|
}
|
|
|
|
return d
|
|
}
|
|
|
|
// encode data as one or more segments and return the encoded data.
|
|
//
|
|
// The returned data does not include the terminator bit sequence.
|
|
func (d *dataEncoder) encode(data []byte) (*bitset.Bitset, error) {
|
|
d.data = data
|
|
d.actual = nil
|
|
d.optimised = nil
|
|
|
|
if len(data) == 0 {
|
|
return nil, errors.New("no data to encode")
|
|
}
|
|
|
|
// Classify data into unoptimised segments.
|
|
highestRequiredMode := d.classifyDataModes()
|
|
|
|
// Optimise segments.
|
|
err := d.optimiseDataModes()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Check if a single byte encoded segment would be more efficient.
|
|
optimizedLength := 0
|
|
for _, s := range d.optimised {
|
|
length, err := d.encodedLength(s.dataMode, len(s.data))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
optimizedLength += length
|
|
}
|
|
|
|
singleByteSegmentLength, err := d.encodedLength(highestRequiredMode, len(d.data))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if singleByteSegmentLength <= optimizedLength {
|
|
d.optimised = []segment{segment{dataMode: highestRequiredMode, data: d.data}}
|
|
}
|
|
|
|
// Encode data.
|
|
encoded := bitset.New()
|
|
for _, s := range d.optimised {
|
|
d.encodeDataRaw(s.data, s.dataMode, encoded)
|
|
}
|
|
|
|
return encoded, nil
|
|
}
|
|
|
|
// classifyDataModes classifies the raw data into unoptimised segments.
|
|
// e.g. "123ZZ#!#!" =>
|
|
// [numeric, 3, "123"] [alphanumeric, 2, "ZZ"] [byte, 4, "#!#!"].
|
|
//
|
|
// Returns the highest data mode needed to encode the data. e.g. for a mixed
|
|
// numeric/alphanumeric input, the highest is alphanumeric.
|
|
//
|
|
// dataModeNone < dataModeNumeric < dataModeAlphanumeric < dataModeByte
|
|
func (d *dataEncoder) classifyDataModes() dataMode {
|
|
var start int
|
|
mode := dataModeNone
|
|
highestRequiredMode := mode
|
|
|
|
for i, v := range d.data {
|
|
newMode := dataModeNone
|
|
switch {
|
|
case v >= 0x30 && v <= 0x39:
|
|
newMode = dataModeNumeric
|
|
case v == 0x20 || v == 0x24 || v == 0x25 || v == 0x2a || v == 0x2b || v ==
|
|
0x2d || v == 0x2e || v == 0x2f || v == 0x3a || (v >= 0x41 && v <= 0x5a):
|
|
newMode = dataModeAlphanumeric
|
|
default:
|
|
newMode = dataModeByte
|
|
}
|
|
|
|
if newMode != mode {
|
|
if i > 0 {
|
|
d.actual = append(d.actual, segment{dataMode: mode, data: d.data[start:i]})
|
|
|
|
start = i
|
|
}
|
|
|
|
mode = newMode
|
|
}
|
|
|
|
if newMode > highestRequiredMode {
|
|
highestRequiredMode = newMode
|
|
}
|
|
}
|
|
|
|
d.actual = append(d.actual, segment{dataMode: mode, data: d.data[start:len(d.data)]})
|
|
|
|
return highestRequiredMode
|
|
}
|
|
|
|
// optimiseDataModes optimises the list of segments to reduce the overall output
|
|
// encoded data length.
|
|
//
|
|
// The algorithm coalesces adjacent segments. segments are only coalesced when
|
|
// the Data Modes are compatible, and when the coalesced segment has a shorter
|
|
// encoded length than separate segments.
|
|
//
|
|
// Multiple segments may be coalesced. For example a string of alternating
|
|
// alphanumeric/numeric segments ANANANANA can be optimised to just A.
|
|
func (d *dataEncoder) optimiseDataModes() error {
|
|
for i := 0; i < len(d.actual); {
|
|
mode := d.actual[i].dataMode
|
|
numChars := len(d.actual[i].data)
|
|
|
|
j := i + 1
|
|
for j < len(d.actual) {
|
|
nextNumChars := len(d.actual[j].data)
|
|
nextMode := d.actual[j].dataMode
|
|
|
|
if nextMode > mode {
|
|
break
|
|
}
|
|
|
|
coalescedLength, err := d.encodedLength(mode, numChars+nextNumChars)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
seperateLength1, err := d.encodedLength(mode, numChars)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
seperateLength2, err := d.encodedLength(nextMode, nextNumChars)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if coalescedLength < seperateLength1+seperateLength2 {
|
|
j++
|
|
numChars += nextNumChars
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
|
|
optimised := segment{dataMode: mode,
|
|
data: make([]byte, 0, numChars)}
|
|
|
|
for k := i; k < j; k++ {
|
|
optimised.data = append(optimised.data, d.actual[k].data...)
|
|
}
|
|
|
|
d.optimised = append(d.optimised, optimised)
|
|
|
|
i = j
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// encodeDataRaw encodes data in dataMode. The encoded data is appended to
|
|
// encoded.
|
|
func (d *dataEncoder) encodeDataRaw(data []byte, dataMode dataMode, encoded *bitset.Bitset) {
|
|
modeIndicator := d.modeIndicator(dataMode)
|
|
charCountBits := d.charCountBits(dataMode)
|
|
|
|
// Append mode indicator.
|
|
encoded.Append(modeIndicator)
|
|
|
|
// Append character count.
|
|
encoded.AppendUint32(uint32(len(data)), charCountBits)
|
|
|
|
// Append data.
|
|
switch dataMode {
|
|
case dataModeNumeric:
|
|
for i := 0; i < len(data); i += 3 {
|
|
charsRemaining := len(data) - i
|
|
|
|
var value uint32
|
|
bitsUsed := 1
|
|
|
|
for j := 0; j < charsRemaining && j < 3; j++ {
|
|
value *= 10
|
|
value += uint32(data[i+j] - 0x30)
|
|
bitsUsed += 3
|
|
}
|
|
encoded.AppendUint32(value, bitsUsed)
|
|
}
|
|
case dataModeAlphanumeric:
|
|
for i := 0; i < len(data); i += 2 {
|
|
charsRemaining := len(data) - i
|
|
|
|
var value uint32
|
|
for j := 0; j < charsRemaining && j < 2; j++ {
|
|
value *= 45
|
|
value += encodeAlphanumericCharacter(data[i+j])
|
|
}
|
|
|
|
bitsUsed := 6
|
|
if charsRemaining > 1 {
|
|
bitsUsed = 11
|
|
}
|
|
|
|
encoded.AppendUint32(value, bitsUsed)
|
|
}
|
|
case dataModeByte:
|
|
for _, b := range data {
|
|
encoded.AppendByte(b, 8)
|
|
}
|
|
}
|
|
}
|
|
|
|
// modeIndicator returns the segment header bits for a segment of type dataMode.
|
|
func (d *dataEncoder) modeIndicator(dataMode dataMode) *bitset.Bitset {
|
|
switch dataMode {
|
|
case dataModeNumeric:
|
|
return d.numericModeIndicator
|
|
case dataModeAlphanumeric:
|
|
return d.alphanumericModeIndicator
|
|
case dataModeByte:
|
|
return d.byteModeIndicator
|
|
default:
|
|
log.Panic("Unknown data mode")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// charCountBits returns the number of bits used to encode the length of a data
|
|
// segment of type dataMode.
|
|
func (d *dataEncoder) charCountBits(dataMode dataMode) int {
|
|
switch dataMode {
|
|
case dataModeNumeric:
|
|
return d.numNumericCharCountBits
|
|
case dataModeAlphanumeric:
|
|
return d.numAlphanumericCharCountBits
|
|
case dataModeByte:
|
|
return d.numByteCharCountBits
|
|
default:
|
|
log.Panic("Unknown data mode")
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
// encodedLength returns the number of bits required to encode n symbols in
|
|
// dataMode.
|
|
//
|
|
// The number of bits required is affected by:
|
|
// - QR code type - Mode Indicator length.
|
|
// - Data mode - number of bits used to represent data length.
|
|
// - Data mode - how the data is encoded.
|
|
// - Number of symbols encoded.
|
|
//
|
|
// An error is returned if the mode is not supported, or the length requested is
|
|
// too long to be represented.
|
|
func (d *dataEncoder) encodedLength(dataMode dataMode, n int) (int, error) {
|
|
modeIndicator := d.modeIndicator(dataMode)
|
|
charCountBits := d.charCountBits(dataMode)
|
|
|
|
if modeIndicator == nil {
|
|
return 0, errors.New("mode not supported")
|
|
}
|
|
|
|
maxLength := (1 << uint8(charCountBits)) - 1
|
|
|
|
if n > maxLength {
|
|
return 0, errors.New("length too long to be represented")
|
|
}
|
|
|
|
length := modeIndicator.Len() + charCountBits
|
|
|
|
switch dataMode {
|
|
case dataModeNumeric:
|
|
length += 10 * (n / 3)
|
|
|
|
if n%3 != 0 {
|
|
length += 1 + 3*(n%3)
|
|
}
|
|
case dataModeAlphanumeric:
|
|
length += 11 * (n / 2)
|
|
length += 6 * (n % 2)
|
|
case dataModeByte:
|
|
length += 8 * n
|
|
}
|
|
|
|
return length, nil
|
|
}
|
|
|
|
// encodeAlphanumericChar returns the QR Code encoded value of v.
|
|
//
|
|
// v must be a QR Code defined alphanumeric character: 0-9, A-Z, SP, $%*+-./ or
|
|
// :. The characters are mapped to values in the range 0-44 respectively.
|
|
func encodeAlphanumericCharacter(v byte) uint32 {
|
|
c := uint32(v)
|
|
|
|
switch {
|
|
case c >= '0' && c <= '9':
|
|
// 0-9 encoded as 0-9.
|
|
return c - '0'
|
|
case c >= 'A' && c <= 'Z':
|
|
// A-Z encoded as 10-35.
|
|
return c - 'A' + 10
|
|
case c == ' ':
|
|
return 36
|
|
case c == '$':
|
|
return 37
|
|
case c == '%':
|
|
return 38
|
|
case c == '*':
|
|
return 39
|
|
case c == '+':
|
|
return 40
|
|
case c == '-':
|
|
return 41
|
|
case c == '.':
|
|
return 42
|
|
case c == '/':
|
|
return 43
|
|
case c == ':':
|
|
return 44
|
|
default:
|
|
log.Panicf("encodeAlphanumericCharacter() with non alphanumeric char %v.", v)
|
|
}
|
|
|
|
return 0
|
|
}
|