From ab1de758f8c125d1bb305f444b5ff7a5060a5164 Mon Sep 17 00:00:00 2001 From: Philip Heron Date: Sun, 4 Mar 2012 23:36:37 +0000 Subject: [PATCH] Update SSDV encoder to latest version --- hadie.c | 2 +- ssdv.c | 380 +++++++++++++++++++++++++++++++++++++++++++++----------- ssdv.h | 74 +++++++---- 3 files changed, 359 insertions(+), 97 deletions(-) diff --git a/hadie.c b/hadie.c index fbf38ec..c618523 100644 --- a/hadie.c +++ b/hadie.c @@ -53,7 +53,7 @@ char tx_image(void) setup = -1; - ssdv_enc_init(&ssdv, img_id++); + ssdv_enc_init(&ssdv, CALLSIGN, img_id++); ssdv_enc_set_buffer(&ssdv, pkt); } diff --git a/ssdv.c b/ssdv.c index a594c95..1f94e2a 100644 --- a/ssdv.c +++ b/ssdv.c @@ -1,17 +1,25 @@ -/* hadie - High Altitude Balloon flight software */ -/*============================================================*/ -/* Copyright (C)2010 Philip Heron */ -/* */ -/* This program is distributed under the terms of the GNU */ -/* General Public License, version 2. You may use, modify, */ -/* and redistribute it under the terms of this license. A */ -/* copy should be included with this source. */ -#include -#include +/* SSDV - Slow Scan Digital Video */ +/*=======================================================================*/ +/* Copyright 2011-2012 Philip Heron . */ + #include #include #include +#include #include "ssdv.h" #include "rs8.h" @@ -28,18 +36,35 @@ enum { J_LSE, J_JPG9, J_JPG10, J_JPG11, J_JPG12, J_JPG13, J_COM, } jpeg_marker_t; -/* Huffman tables for output (and for the C328 camera, input also) */ -static uint8_t dht00[29] = { +/* Quantisation tables */ +PROGMEM static const uint8_t const std_dqt0[65] = { +0x00,0x10,0x0C,0x0C,0x0E,0x0C,0x0A,0x10,0x0E,0x0E,0x0E,0x12,0x12,0x10,0x14,0x18, +0x28,0x1A,0x18,0x16,0x16,0x18,0x32,0x24,0x26,0x1E,0x28,0x3A,0x34,0x3E,0x3C,0x3A, +0x34,0x38,0x38,0x40,0x48,0x5C,0x4E,0x40,0x44,0x58,0x46,0x38,0x38,0x50,0x6E,0x52, +0x58,0x60,0x62,0x68,0x68,0x68,0x3E,0x4E,0x72,0x7A,0x70,0x64,0x78,0x5C,0x66,0x68, +0x64, +}; + +PROGMEM static const uint8_t const std_dqt1[65] = { +0x01,0x12,0x12,0x12,0x16,0x16,0x16,0x30,0x1A,0x1A,0x30,0x64,0x42,0x38,0x42,0x64, +0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64, +0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64, +0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64, +0x64, +}; + +/* Standard Huffman tables */ +PROGMEM static const uint8_t const std_dht00[29] = { 0x00,0x00,0x01,0x05,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B, }; -static uint8_t dht01[29] = { +PROGMEM static const uint8_t const std_dht01[29] = { 0x01,0x00,0x03,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x00,0x00, 0x00,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B, }; -static uint8_t dht10[179] = { +PROGMEM static const uint8_t const std_dht10[179] = { 0x10,0x00,0x02,0x01,0x03,0x03,0x02,0x04,0x03,0x05,0x05,0x04,0x04,0x00,0x00,0x01, 0x7D,0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61, 0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xA1,0x08,0x23,0x42,0xB1,0xC1,0x15,0x52,0xD1, @@ -54,7 +79,7 @@ static uint8_t dht10[179] = { 0xF8,0xF9,0xFA, }; -static uint8_t dht11[179] = { +PROGMEM static const uint8_t const std_dht11[179] = { 0x11,0x00,0x02,0x01,0x02,0x04,0x04,0x03,0x04,0x07,0x05,0x04,0x04,0x00,0x01,0x02, 0x77,0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61, 0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,0xA1,0xB1,0xC1,0x09,0x23,0x33,0x52, @@ -69,8 +94,56 @@ static uint8_t dht11[179] = { 0xF8,0xF9,0xFA, }; -static uint8_t *dht_dc[3] = { dht00, dht01, dht01 }; -static uint8_t *dht_ac[3] = { dht10, dht11, dht11 }; +/* Helper for returning the current DHT table */ +#define SDHT (s->sdht[s->acpart ? 1 : 0][s->component ? 1 : 0]) +#define DDHT (s->ddht[s->acpart ? 1 : 0][s->component ? 1 : 0]) + +/* Helpers for looking up the current DQT value */ +#define SDQT (s->sdqt[s->component ? 1 : 0][1 + s->acpart]) +#define DDQT (s->ddqt[s->component ? 1 : 0][1 + s->acpart]) + +/* Helpers for converting between DQT tables */ +#define AADJ(i) (SDQT == DDQT ? (i) : irdiv(i, DDQT)) +#define UADJ(i) (SDQT == DDQT ? (i) : (i * SDQT)) +#define BADJ(i) (SDQT == DDQT ? (i) : irdiv(i * SDQT, DDQT)) + +/* Integer-only division with rounding */ +static int irdiv(int i, int div) +{ + i = i * 2 / div; + if(i & 1) i += (i > 0 ? 1 : -1); + return(i / 2); +} + +static void *dtblcpy(ssdv_t *s, const void *src, size_t n) +{ + void *r; + if(s->dtbl_len + n > TBL_LEN) return(NULL); + r = memcpy_P(&s->dtbls[s->dtbl_len], src, n); + s->dtbl_len += n; + return(r); +} + +static uint32_t encode_callsign(char *callsign) +{ + uint32_t x; + char *c; + + /* Point c at the end of the callsign */ + for(c = callsign; *c; c++); + + /* Encode it backwards */ + x = 0; + for(c--; c >= callsign; c--) + { + x *= 40; + if(*c >= 'A' && *c <= 'Z') x += *c - 'A' + 14; + else if(*c >= 'a' && *c <= 'z') x += *c - 'a' + 14; + else if(*c >= '0' && *c <= '9') x += *c - '0' + 1; + } + + return(x); +} static inline char jpeg_dht_lookup(ssdv_t *s, uint8_t *symbol, uint8_t *width) { @@ -79,8 +152,7 @@ static inline char jpeg_dht_lookup(ssdv_t *s, uint8_t *symbol, uint8_t *width) uint8_t *dht, *ss; /* Select the appropriate huffman table */ - if(s->acpart == 0) dht = dht_dc[s->component]; - else dht = dht_ac[s->component]; + dht = SDHT; ss = &dht[17]; for(cw = 1; cw <= 16; cw++) @@ -108,10 +180,14 @@ static inline char jpeg_dht_lookup(ssdv_t *s, uint8_t *symbol, uint8_t *width) return(SSDV_ERROR); } -static inline char jpeg_dht_lookup_symbol(uint8_t *dht, uint8_t symbol, uint16_t *bits, uint8_t *width) +static inline char jpeg_dht_lookup_symbol(ssdv_t *s, uint8_t symbol, uint16_t *bits, uint8_t *width) { uint16_t code = 0; - uint8_t cw, n, *ss = &dht[17]; + uint8_t cw, n; + uint8_t *dht, *ss; + + dht = DDHT; + ss = &dht[17]; for(cw = 1; cw <= 16; cw++) { @@ -191,16 +267,9 @@ static char ssdv_out_jpeg_int(ssdv_t *s, uint8_t rle, int value) uint16_t huffbits = 0; int intbits; uint8_t hufflen = 0, intlen; - uint8_t *dht; - int r; - - /* Select the appropriate huffman table */ - /* TODO: Do this elsewhere */ - if(s->acpart == 0) dht = dht_dc[s->component]; - else dht = dht_ac[s->component]; jpeg_encode_int(value, &intbits, &intlen); - r = jpeg_dht_lookup_symbol(dht, (rle << 4) | (intlen & 0x0F), &huffbits, &hufflen); + jpeg_dht_lookup_symbol(s, (rle << 4) | (intlen & 0x0F), &huffbits, &hufflen); ssdv_outbits(s, huffbits, hufflen); if(intlen) ssdv_outbits(s, intbits, intlen); @@ -224,8 +293,10 @@ static char ssdv_process(ssdv_t *s) if(symbol == 0x00) { /* No change in DC from last block */ - if(s->mcupart == 0 || s->mcupart == 4 || s->mcupart == 5) - ssdv_out_jpeg_int(s, 0, s->dc[s->component]); + if(s->reset_mcu == s->mcu_id && (s->mcupart == 0 || s->mcupart >= s->ycparts)) + { + ssdv_out_jpeg_int(s, 0, s->adc[s->component]); + } else ssdv_out_jpeg_int(s, 0, 0); /* skip to the next AC part immediately */ @@ -241,7 +312,6 @@ static char ssdv_process(ssdv_t *s) else /* AC */ { s->acrle = 0; - if(symbol == 0x00) { /* EOB -- all remaining AC parts are zero */ @@ -280,23 +350,47 @@ static char ssdv_process(ssdv_t *s) if(s->acpart == 0) /* DC */ { - s->dc[s->component] += i; - - if(s->mcupart == 0 || s->mcupart == 4 || s->mcupart == 5) + if(s->reset_mcu == s->mcu_id && (s->mcupart == 0 || s->mcupart >= s->ycparts)) { /* Output absolute DC value */ - ssdv_out_jpeg_int(s, 0, s->dc[s->component]); + s->dc[s->component] += UADJ(i); + s->adc[s->component] = AADJ(s->dc[s->component]); + ssdv_out_jpeg_int(s, 0, s->adc[s->component]); } else { /* Output relative DC value */ - ssdv_out_jpeg_int(s, 0, i); + s->dc[s->component] += UADJ(i); + + /* Calculate closest adjusted DC value */ + i = AADJ(s->dc[s->component]); + ssdv_out_jpeg_int(s, 0, i - s->adc[s->component]); + s->adc[s->component] = i; } } else /* AC */ { - /* Output AC codes directly */ - ssdv_out_jpeg_int(s, s->acrle, i); + if((i = BADJ(i))) + { + s->accrle += s->acrle; + while(s->accrle >= 16) + { + ssdv_out_jpeg_int(s, 15, 0); + s->accrle -= 16; + } + ssdv_out_jpeg_int(s, s->accrle, i); + s->accrle = 0; + } + else + { + /* AC value got reduced to 0 in the DQT conversion */ + if(s->acpart >= 63) + { + ssdv_out_jpeg_int(s, 0, 0); + s->accrle = 0; + } + else s->accrle += s->acrle + 1; + } } /* Next AC part to expect */ @@ -313,7 +407,7 @@ static char ssdv_process(ssdv_t *s) if(s->acpart >= 64) { /* Reached the end of this MCU part */ - if(++s->mcupart == 6) + if(++s->mcupart == s->ycparts + 2) { s->mcupart = 0; s->mcu_id++; @@ -321,6 +415,7 @@ static char ssdv_process(ssdv_t *s) /* Test for the end of image */ if(s->mcu_id >= s->mcu_count) { + /* Flush any remaining bits */ ssdv_outbits_sync(s); return(SSDV_EOI); } @@ -328,16 +423,27 @@ static char ssdv_process(ssdv_t *s) /* Set the packet MCU marker - encoder only */ if(s->packet_mcu_id == 0xFFFF) { + /* The first MCU of each packet should be byte aligned */ + ssdv_outbits_sync(s); + + s->reset_mcu = s->mcu_id; s->packet_mcu_id = s->mcu_id; - s->packet_mcu_offset = - (SSDV_PKT_SIZE_PAYLOAD - s->out_len) * 8 + s->outlen; + s->packet_mcu_offset = SSDV_PKT_SIZE_PAYLOAD - s->out_len; + } + + /* Test for a reset marker */ + if(s->dri > 0 && s->mcu_id > 0 && s->mcu_id % s->dri == 0) + { + s->state = S_MARKER; + return(SSDV_FEED_ME); } } - if(s->mcupart < 4) s->component = 0; - else s->component = s->mcupart - 3; + if(s->mcupart < s->ycparts) s->component = 0; + else s->component = s->mcupart - s->ycparts + 1; s->acpart = 0; + s->accrle = 0; } if(s->out_len == 0) return(SSDV_BUFFER_FULL); @@ -360,22 +466,44 @@ static char ssdv_have_marker(ssdv_t *s) { case J_SOF0: case J_SOS: + case J_DRI: + case J_DHT: + case J_DQT: /* Copy the data before processing */ - if(s->marker_len > HBUFF_LEN) + if(s->marker_len > TBL_LEN + HBUFF_LEN - s->stbl_len) { /* Not enough memory ... shouldn't happen! */ return(SSDV_ERROR); } - s->marker_data = s->hbuff; + s->marker_data = &s->stbls[s->stbl_len]; s->marker_data_len = 0; s->state = S_MARKER_DATA; break; + case J_SOF2: + /* Don't do progressive images! */ + return(SSDV_ERROR); + case J_EOI: s->state = S_EOI; break; + case J_RST0: + case J_RST1: + case J_RST2: + case J_RST3: + case J_RST4: + case J_RST5: + case J_RST6: + case J_RST7: + s->dc[0] = s->dc[1] = s->dc[2] = 0; + s->mcupart = s->acpart = s->component = 0; + s->acrle = s->accrle = 0; + s->workbits = s->worklen = 0; + s->state = S_HUFF; + break; + default: /* Ignore other marks, skipping any associated data */ s->in_skip = s->marker_len; @@ -389,6 +517,8 @@ static char ssdv_have_marker(ssdv_t *s) static char ssdv_have_marker_data(ssdv_t *s) { uint8_t *d = s->marker_data; + size_t l = s->marker_len; + int i; switch(s->marker) { @@ -396,38 +526,141 @@ static char ssdv_have_marker_data(ssdv_t *s) s->width = (d[3] << 8) | d[4]; s->height = (d[1] << 8) | d[2]; - /* Calculate number of MCU blocks in this image -- assumes 16x16 blocks */ - s->mcu_count = (s->width >> 4) * (s->height >> 4); - /* The image must have a precision of 8 */ if(d[0] != 8) return(SSDV_ERROR); /* The image must have 3 components (Y'Cb'Cr) */ if(d[5] != 3) return(SSDV_ERROR); + /* Maximum image is 4080x4080 */ + if(s->width > 4080 || s->height > 4080) return(SSDV_ERROR); + /* The image dimensions must be a multiple of 16 */ if((s->width & 0x0F) || (s->height & 0x0F)) return(SSDV_ERROR); + /* TODO: Read in the quantisation table ID for each component */ + // 01 22 00 02 11 01 03 11 01 + for(i = 0; i < 3; i++) + { + uint8_t *dq = &d[i * 3 + 6]; + if(dq[0] != i + 1) return(SSDV_ERROR); + + /* The first (Y) component must have a factor of 2x2,2x1,1x2 or 1x1 */ + if(dq[0] == 1) + { + switch(dq[1]) + { + case 0x22: s->mcu_mode = 0; s->ycparts = 4; break; + case 0x12: s->mcu_mode = 1; s->ycparts = 2; break; + case 0x21: s->mcu_mode = 2; s->ycparts = 2; break; + case 0x11: s->mcu_mode = 3; s->ycparts = 1; break; + default: return(SSDV_ERROR); + } + } + else if(dq[0] != 1 && dq[1] != 0x11) return(SSDV_ERROR); + } + + /* Calculate number of MCU blocks in this image */ + switch(s->mcu_mode) + { + case 0: l = (s->width >> 4) * (s->height >> 4); break; + case 1: l = (s->width >> 4) * (s->height >> 3); break; + case 2: l = (s->width >> 3) * (s->height >> 4); break; + case 3: l = (s->width >> 3) * (s->height >> 3); break; + } + + if(l > 0xFFFF) return(SSDV_ERROR); + + s->mcu_count = l; + break; case J_SOS: /* The image must have 3 components (Y'Cb'Cr) */ if(d[0] != 3) return(SSDV_ERROR); + for(i = 0; i < 3; i++) + { + uint8_t *dh = &d[i * 2 + 1]; + if(dh[0] != i + 1) return(SSDV_ERROR); + } + + /* Do I need to look at the last three bytes of the SOS data? */ + /* 00 3F 00 */ + + /* Verify all of the DQT and DHT tables where loaded */ + if(!s->sdqt[0] || !s->sdqt[1]) return(SSDV_ERROR); + + if(!s->sdht[0][0] || !s->sdht[0][1] || + !s->sdht[1][0] || !s->sdht[1][1]) return(SSDV_ERROR); + /* The SOS data is followed by the image data */ s->state = S_HUFF; return(SSDV_OK); + + case J_DHT: + s->stbl_len += l; + while(l > 0) + { + int i, j; + + switch(d[0]) + { + case 0x00: s->sdht[0][0] = d; break; + case 0x01: s->sdht[0][1] = d; break; + case 0x10: s->sdht[1][0] = d; break; + case 0x11: s->sdht[1][1] = d; break; + } + + /* Skip to the next DHT table */ + for(j = 17, i = 1; i <= 16; i++) + j += d[i]; + + l -= j; + d += j; + } + break; + + case J_DQT: + s->stbl_len += l; + while(l > 0) + { + switch(d[0]) + { + case 0x00: s->sdqt[0] = d; break; + case 0x01: s->sdqt[1] = d; break; + } + + /* Skip to the next one, if present */ + l -= 65; + d += 65; + } + break; + + case J_DRI: + s->dri = (d[0] << 8) + d[1]; + break; } s->state = S_MARKER; return(SSDV_OK); } -char ssdv_enc_init(ssdv_t *s, uint8_t image_id) +char ssdv_enc_init(ssdv_t *s, char *callsign, uint8_t image_id) { memset(s, 0, sizeof(ssdv_t)); s->image_id = image_id; + s->callsign = encode_callsign(callsign); + + /* Prepare the output JPEG tables */ + s->ddqt[0] = dtblcpy(s, std_dqt0, sizeof(std_dqt0)); + s->ddqt[1] = dtblcpy(s, std_dqt1, sizeof(std_dqt1)); + s->ddht[0][0] = dtblcpy(s, std_dht00, sizeof(std_dht00)); + s->ddht[0][1] = dtblcpy(s, std_dht01, sizeof(std_dht01)); + s->ddht[1][0] = dtblcpy(s, std_dht10, sizeof(std_dht10)); + s->ddht[1][1] = dtblcpy(s, std_dht11, sizeof(std_dht11)); + return(SSDV_OK); } @@ -437,6 +670,9 @@ char ssdv_enc_set_buffer(ssdv_t *s, uint8_t *buffer) s->outp = buffer + SSDV_PKT_SIZE_HEADER; s->out_len = SSDV_PKT_SIZE_PAYLOAD; + /* Zero the payload memory */ + memset(s->out, 0, SSDV_PKT_SIZE); + /* Flush the output bits */ ssdv_outbits(s, 0, 0); @@ -449,7 +685,7 @@ char ssdv_enc_get_packet(ssdv_t *s) uint8_t b; /* Have we reached the end of the image? */ - if(s->state == S_EOI) return(SSDV_EOI); + if(s->state == S_EOI) return(SSDV_EOI); /* If the output buffer is empty, re-initialise */ if(s->out_len == 0) ssdv_enc_set_buffer(s, s->out); @@ -519,39 +755,45 @@ char ssdv_enc_get_packet(ssdv_t *s) if(r == SSDV_BUFFER_FULL || r == SSDV_EOI) { uint16_t mcu_id = s->packet_mcu_id; - uint16_t mcu_offset = s->packet_mcu_offset; + uint8_t mcu_offset = s->packet_mcu_offset; uint16_t i, x; - if(mcu_offset != 0xFFFF && mcu_offset >= SSDV_PKT_SIZE_PAYLOAD * 8) + if(mcu_offset != 0xFF && mcu_offset >= SSDV_PKT_SIZE_PAYLOAD) { /* The first MCU begins in the next packet, not this one */ - mcu_id = mcu_offset = 0xFFFF; - s->packet_mcu_offset -= SSDV_PKT_SIZE_PAYLOAD * 8; + mcu_id = 0xFFFF; + mcu_offset = 0xFF; + s->packet_mcu_offset -= SSDV_PKT_SIZE_PAYLOAD; } else { /* Clear the MCU data for the next packet */ - s->packet_mcu_id = s->packet_mcu_offset = 0xFFFF; + s->packet_mcu_id = 0xFFFF; + s->packet_mcu_offset = 0xFF; } /* A packet is ready, create the headers */ s->out[0] = 0x55; /* Sync */ s->out[1] = 0x66; /* Type */ - s->out[2] = s->image_id; /* Image ID */ - s->out[3] = s->packet_id >> 8; /* Packet ID MSB */ - s->out[4] = s->packet_id & 0xFF; /* Packet ID LSB */ - s->out[5] = s->width >> 4; /* Width / 16 */ - s->out[6] = s->height >> 4; /* Height / 16 */ - s->out[7] = mcu_offset >> 8; /* Next MCU offset MSB */ - s->out[8] = mcu_offset & 0xFF; /* Next MCU offset LSB */ - s->out[9] = mcu_id >> 8; /* MCU ID MSB */ - s->out[10] = mcu_id & 0xFF; /* MCU ID LSB */ + s->out[2] = s->callsign >> 24; + s->out[3] = s->callsign >> 16; + s->out[4] = s->callsign >> 8; + s->out[5] = s->callsign; + s->out[6] = s->image_id; /* Image ID */ + s->out[7] = s->packet_id >> 8; /* Packet ID MSB */ + s->out[8] = s->packet_id & 0xFF; /* Packet ID LSB */ + s->out[9] = s->width >> 4; /* Width / 16 */ + s->out[10] = s->height >> 4; /* Height / 16 */ + s->out[11] = s->mcu_mode & 0x03; /* MCU mode (2 bits) */ + s->out[12] = mcu_offset; /* Next MCU offset */ + s->out[13] = mcu_id >> 8; /* MCU ID MSB */ + s->out[14] = mcu_id & 0xFF; /* MCU ID LSB */ /* Fill any remaining bytes with noise */ if(s->out_len > 0) ssdv_memset_prng(s->outp, s->out_len); /* Calculate the CRC codes */ - for(i = 1, x = 0xFFFF; i < SSDV_PKT_SIZE - SSDV_PKT_SIZE_RSCODES - SSDV_PKT_SIZE_CHECKSUM; i++) + for(i = 1, x = 0xFFFF; i < SSDV_PKT_SIZE - SSDV_PKT_SIZE_RSCODES - SSDV_PKT_SIZE_CRC; i++) x = _crc_xmodem_update(x, s->out[i]); s->out[i++] = x >> 8; @@ -567,11 +809,7 @@ char ssdv_enc_get_packet(ssdv_t *s) return(SSDV_OK); } - else if(r != SSDV_FEED_ME) - { - /* An error occured */ - return(SSDV_ERROR); - } + else if(r != SSDV_FEED_ME) return(SSDV_ERROR); break; case S_EOI: diff --git a/ssdv.h b/ssdv.h index 1c96cef..e85d8cd 100644 --- a/ssdv.h +++ b/ssdv.h @@ -1,11 +1,20 @@ -/* hadie - High Altitude Balloon flight software */ -/*============================================================*/ -/* Copyright (C)2010 Philip Heron */ -/* */ -/* This program is distributed under the terms of the GNU */ -/* General Public License, version 2. You may use, modify, */ -/* and redistribute it under the terms of this license. A */ -/* copy should be included with this source. */ + +/* SSDV - Slow Scan Digital Video */ +/*=======================================================================*/ +/* Copyright 2011-2012 Philip Heron . */ #include @@ -21,25 +30,28 @@ /* Packet details */ #define SSDV_PKT_SIZE (0x100) -#define SSDV_PKT_SIZE_HEADER (0x0B) -#define SSDV_PKT_SIZE_CHECKSUM (0x02) -#define SSDV_PKT_SIZE_RSCODES (0x20) -#define SSDV_PKT_SIZE_PAYLOAD (SSDV_PKT_SIZE - SSDV_PKT_SIZE_HEADER - SSDV_PKT_SIZE_CHECKSUM - SSDV_PKT_SIZE_RSCODES) +#define SSDV_PKT_SIZE_HEADER (0x0F) +#define SSDV_PKT_SIZE_CRC (0x02) +#define SSDV_PKT_SIZE_RSCODES (0x20) +#define SSDV_PKT_SIZE_PAYLOAD (SSDV_PKT_SIZE - SSDV_PKT_SIZE_HEADER - SSDV_PKT_SIZE_CRC - SSDV_PKT_SIZE_RSCODES) -#define HBUFF_LEN (16) -#define COMPONENTS (3) +#define TBL_LEN (546) /* Maximum size of the DQT and DHT tables */ +#define HBUFF_LEN (16) /* Extra space for reading marker data */ +//#define COMPONENTS (3) typedef struct { /* Image information */ uint16_t width; uint16_t height; - uint8_t image_id; + uint32_t callsign; + uint8_t image_id; uint16_t packet_id; + uint8_t mcu_mode; /* 0 = 2x2, 1 = 2x1, 2 = 1x2, 3 = 1x1 */ uint16_t mcu_id; uint16_t mcu_count; uint16_t packet_mcu_id; - uint16_t packet_mcu_offset; + uint8_t packet_mcu_offset; /* Source buffer */ uint8_t *inp; /* Pointer to next input byte */ @@ -66,26 +78,38 @@ typedef struct S_MARKER_DATA, S_HUFF, S_INT, - S_EOI + S_EOI, } state; uint16_t marker; /* Current marker */ uint16_t marker_len; /* Length of data following marker */ uint8_t *marker_data; /* Where to copy marker data too */ uint16_t marker_data_len; /* How much is there */ - unsigned char component; /* 0 = Y, 1 = Cb, 2 = Cr */ - unsigned char mcupart; /* 0-3 = Y, 4 = Cb, 5 = Cr */ - unsigned char acpart; /* 0 - 64; 0 = DC, 1 - 64 = AC */ - int dc[COMPONENTS]; /* DC value for each component */ + uint8_t component; /* 0 = Y, 1 = Cb, 2 = Cr */ + uint8_t ycparts; /* Number of Y component parts per MCU */ + uint8_t mcupart; /* 0-3 = Y, 4 = Cb, 5 = Cr */ + uint8_t acpart; /* 0 - 64; 0 = DC, 1 - 64 = AC */ + int dc[3]; /* DC value for each component */ + int adc[3]; /* DC adjusted value for each component */ uint8_t acrle; /* RLE value for current AC value */ - unsigned char needbits; /* Number of bits needed to decode integer */ + uint8_t accrle; /* Accumulative RLE value */ + uint16_t dri; /* Reset interval */ + uint32_t reset_mcu; /* MCU block to do absolute encoding */ + char needbits; /* Number of bits needed to decode integer */ - /* Small buffer for reading SOF0 and SOS header data into */ - uint8_t hbuff[HBUFF_LEN]; + /* The input huffman and quantisation tables */ + uint8_t stbls[TBL_LEN + HBUFF_LEN]; + uint8_t *sdht[2][2], *sdqt[2]; + uint16_t stbl_len; + + /* The same for output */ + uint8_t dtbls[TBL_LEN]; + uint8_t *ddht[2][2], *ddqt[2]; + uint16_t dtbl_len; } ssdv_t; /* Encoding */ -extern char ssdv_enc_init(ssdv_t *s, uint8_t image_id); +extern char ssdv_enc_init(ssdv_t *s, char *callsign, uint8_t image_id); extern char ssdv_enc_set_buffer(ssdv_t *s, uint8_t *buffer); extern char ssdv_enc_get_packet(ssdv_t *s); extern char ssdv_enc_feed(ssdv_t *s, uint8_t *buffer, size_t length);