Add support for multiple subsampling formats. Not backwards compatible!

This commit is contained in:
Philip Heron 2012-02-25 00:25:25 +00:00
parent 604f4f17f5
commit 2df18c50a1
3 changed files with 76 additions and 32 deletions

1
README
View File

@ -15,7 +15,6 @@ TODO
* Allow the decoder to handle multiple images in the input stream. * Allow the decoder to handle multiple images in the input stream.
* Experiment with adaptive or multiple huffman tables. * Experiment with adaptive or multiple huffman tables.
* Support more than one subsampling configuration. (4 modes / 2 bits)
* Quality setting (4 bit / 16 quality levels). * Quality setting (4 bit / 16 quality levels).
* Add a callsign field to each packet. * Add a callsign field to each packet.

100
ssdv.c
View File

@ -1,7 +1,7 @@
/* SSDV - Slow Scan Digital Video */ /* SSDV - Slow Scan Digital Video */
/*=======================================================================*/ /*=======================================================================*/
/* Copyright 2011 Philip Heron <phil@sanslogic.co.uk */ /* Copyright 2011-2012 Philip Heron <phil@sanslogic.co.uk */
/* */ /* */
/* This program is free software: you can redistribute it and/or modify */ /* This program is free software: you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */ /* it under the terms of the GNU General Public License as published by */
@ -314,7 +314,7 @@ static char ssdv_process(ssdv_t *s)
if(symbol == 0x00) if(symbol == 0x00)
{ {
/* No change in DC from last block */ /* No change in DC from last block */
if(s->reset_mcu == s->mcu_id && (s->mcupart == 0 || s->mcupart == 4 || s->mcupart == 5)) if(s->reset_mcu == s->mcu_id && (s->mcupart == 0 || s->mcupart >= s->ycparts))
{ {
if(s->mode == S_ENCODING) ssdv_out_jpeg_int(s, 0, s->adc[s->component]); if(s->mode == S_ENCODING) ssdv_out_jpeg_int(s, 0, s->adc[s->component]);
else else
@ -376,7 +376,7 @@ static char ssdv_process(ssdv_t *s)
if(s->acpart == 0) /* DC */ if(s->acpart == 0) /* DC */
{ {
if(s->reset_mcu == s->mcu_id && (s->mcupart == 0 || s->mcupart == 4 || s->mcupart == 5)) if(s->reset_mcu == s->mcu_id && (s->mcupart == 0 || s->mcupart >= s->ycparts))
{ {
if(s->mode == S_ENCODING) if(s->mode == S_ENCODING)
{ {
@ -450,7 +450,7 @@ static char ssdv_process(ssdv_t *s)
if(s->acpart >= 64) if(s->acpart >= 64)
{ {
/* Reached the end of this MCU part */ /* Reached the end of this MCU part */
if(++s->mcupart == 6) if(++s->mcupart == s->ycparts + 2)
{ {
s->mcupart = 0; s->mcupart = 0;
s->mcu_id++; s->mcu_id++;
@ -480,8 +480,8 @@ static char ssdv_process(ssdv_t *s)
} }
} }
if(s->mcupart < 4) s->component = 0; if(s->mcupart < s->ycparts) s->component = 0;
else s->component = s->mcupart - 3; else s->component = s->mcupart - s->ycparts + 1;
s->acpart = 0; s->acpart = 0;
s->accrle = 0; s->accrle = 0;
@ -574,14 +574,10 @@ static char ssdv_have_marker_data(ssdv_t *s)
s->width = (d[3] << 8) | d[4]; s->width = (d[3] << 8) | d[4];
s->height = (d[1] << 8) | d[2]; 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);
/* Display information about the image... */ /* Display information about the image... */
fprintf(stderr, "Precision: %i\n", d[0]); fprintf(stderr, "Precision: %i\n", d[0]);
fprintf(stderr, "Resolution: %ix%i\n", s->width, s->height); fprintf(stderr, "Resolution: %ix%i\n", s->width, s->height);
fprintf(stderr, "Components: %i\n", d[5]); fprintf(stderr, "Components: %i\n", d[5]);
fprintf(stderr, "MCU blocks: %i\n", s->mcu_count);
/* The image must have a precision of 8 */ /* The image must have a precision of 8 */
if(d[0] != 8) if(d[0] != 8)
@ -624,11 +620,19 @@ static char ssdv_have_marker_data(ssdv_t *s)
fprintf(stderr, "DQT table for component %i: %02X, Sampling factor: %ix%i\n", dq[0], dq[2], dq[1] & 0x0F, dq[1] >> 4); fprintf(stderr, "DQT table for component %i: %02X, Sampling factor: %ix%i\n", dq[0], dq[2], dq[1] & 0x0F, dq[1] >> 4);
/* The first component must have a factor of 2x2 and the rest 1x1 */ /* The first (Y) component must have a factor of 2x2,2x1,1x2 or 1x1 */
if(dq[0] == 1 && dq[1] != 0x22) if(dq[0] == 1)
{ {
fprintf(stderr, "Error: Component 1 sampling factor must be 2x2\n"); switch(dq[1])
return(SSDV_ERROR); {
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:
fprintf(stderr, "Error: Component 1 sampling factor is not supported\n");
return(SSDV_ERROR);
}
} }
else if(dq[0] != 1 && dq[1] != 0x11) else if(dq[0] != 1 && dq[1] != 0x11)
{ {
@ -637,6 +641,17 @@ static char ssdv_have_marker_data(ssdv_t *s)
} }
} }
/* Calculate number of MCU blocks in this image */
switch(s->mcu_mode)
{
case 0: s->mcu_count = (s->width >> 4) * (s->height >> 4); break;
case 1: s->mcu_count = (s->width >> 4) * (s->height >> 3); break;
case 2: s->mcu_count = (s->width >> 3) * (s->height >> 4); break;
case 3: s->mcu_count = (s->width >> 3) * (s->height >> 3); break;
}
fprintf(stderr, "MCU blocks: %i\n", s->mcu_count);
break; break;
case J_SOS: case J_SOS:
@ -838,10 +853,11 @@ char ssdv_enc_get_packet(ssdv_t *s)
s->out[4] = s->packet_id & 0xFF; /* Packet ID LSB */ s->out[4] = s->packet_id & 0xFF; /* Packet ID LSB */
s->out[5] = s->width >> 4; /* Width / 16 */ s->out[5] = s->width >> 4; /* Width / 16 */
s->out[6] = s->height >> 4; /* Height / 16 */ s->out[6] = s->height >> 4; /* Height / 16 */
s->out[7] = mcu_offset >> 8; /* Next MCU offset MSB */ s->out[7] = s->mcu_mode & 0x03; /* MCU mode (2 bits) */
s->out[8] = mcu_offset & 0xFF; /* Next MCU offset LSB */ s->out[8] = mcu_offset >> 8; /* Next MCU offset MSB */
s->out[9] = mcu_id >> 8; /* MCU ID MSB */ s->out[9] = mcu_offset & 0xFF; /* Next MCU offset LSB */
s->out[10] = mcu_id & 0xFF; /* MCU ID LSB */ s->out[10] = mcu_id >> 8; /* MCU ID MSB */
s->out[11] = mcu_id & 0xFF; /* MCU ID LSB */
/* Fill any remaining bytes with noise */ /* Fill any remaining bytes with noise */
if(s->out_len > 0) ssdv_memset_prng(s->outp, s->out_len); if(s->out_len > 0) ssdv_memset_prng(s->outp, s->out_len);
@ -917,7 +933,13 @@ static void ssdv_out_headers(ssdv_t *s)
b[4] = s->width & 0xFF; b[4] = s->width & 0xFF;
b[5] = 3; /* Components (Y'Cb'Cr) */ b[5] = 3; /* Components (Y'Cb'Cr) */
b[6] = 1; /* Y */ b[6] = 1; /* Y */
b[7] = 0x22; switch(s->mcu_mode)
{
case 0: b[7] = 0x22; break;
case 1: b[7] = 0x12; break;
case 2: b[7] = 0x21; break;
case 3: b[7] = 0x11; break;
}
b[8] = 0x00; b[8] = 0x00;
b[9] = 2; /* Cb */ b[9] = 2; /* Cb */
b[10] = 0x11; b[10] = 0x11;
@ -946,10 +968,10 @@ static void ssdv_fill_gap(ssdv_t *s, uint16_t next_mcu)
} }
/* End the current MCU block */ /* End the current MCU block */
for(; s->mcupart < 6; s->mcupart++) for(; s->mcupart < s->ycparts + 2; s->mcupart++)
{ {
if(s->mcupart < 4) s->component = 0; if(s->mcupart < s->ycparts) s->component = 0;
else s->component = s->mcupart - 3; else s->component = s->mcupart - s->ycparts + 1;
s->acpart = 0; ssdv_out_jpeg_int(s, 0, 0); /* DC */ s->acpart = 0; ssdv_out_jpeg_int(s, 0, 0); /* DC */
s->acpart = 1; ssdv_out_jpeg_int(s, 0, 0); /* AC */ s->acpart = 1; ssdv_out_jpeg_int(s, 0, 0); /* AC */
} }
@ -961,10 +983,10 @@ static void ssdv_fill_gap(ssdv_t *s, uint16_t next_mcu)
for(; s->mcu_id < next_mcu; s->mcu_id++) for(; s->mcu_id < next_mcu; s->mcu_id++)
{ {
/* End the current MCU block */ /* End the current MCU block */
for(s->mcupart = 0; s->mcupart < 6; s->mcupart++) for(s->mcupart = 0; s->mcupart < s->ycparts + 2; s->mcupart++)
{ {
if(s->mcupart < 4) s->component = 0; if(s->mcupart < s->ycparts) s->component = 0;
else s->component = s->mcupart - 3; else s->component = s->mcupart - s->ycparts + 1;
s->acpart = 0; ssdv_out_jpeg_int(s, 0, 0); /* DC */ s->acpart = 0; ssdv_out_jpeg_int(s, 0, 0); /* DC */
s->acpart = 1; ssdv_out_jpeg_int(s, 0, 0); /* AC */ s->acpart = 1; ssdv_out_jpeg_int(s, 0, 0); /* AC */
} }
@ -1004,19 +1026,36 @@ char ssdv_dec_feed(ssdv_t *s, uint8_t *packet)
/* Read the packet header */ /* Read the packet header */
packet_id = (packet[3] << 8) | packet[4]; packet_id = (packet[3] << 8) | packet[4];
s->packet_mcu_offset = (packet[7] << 8) | packet[8]; s->packet_mcu_offset = (packet[8] << 8) | packet[9];
s->packet_mcu_id = (packet[9] << 8) | packet[10]; s->packet_mcu_id = (packet[10] << 8) | packet[11];
if(s->packet_mcu_id != 0xFFFF) s->reset_mcu = s->packet_mcu_id; if(s->packet_mcu_id != 0xFFFF) s->reset_mcu = s->packet_mcu_id;
/* If this is the first packet, write the JPEG headers */ /* If this is the first packet, write the JPEG headers */
if(s->packet_id == 0) if(s->packet_id == 0)
{ {
const char *factor;
/* Read the fixed headers from the packet */ /* Read the fixed headers from the packet */
s->image_id = packet[2]; s->image_id = packet[2];
s->width = packet[5] << 4; s->width = packet[5] << 4;
s->height = packet[6] << 4; s->height = packet[6] << 4;
s->mcu_count = packet[5] * packet[6]; s->mcu_count = packet[5] * packet[6];
s->mcu_mode = packet[7] & 0x03;
switch(s->mcu_mode)
{
case 0: factor = "2x2"; s->ycparts = 4; break;
case 1: factor = "1x2"; s->ycparts = 2; s->mcu_count *= 2; break;
case 2: factor = "2x1"; s->ycparts = 2; s->mcu_count *= 2; break;
case 3: factor = "1x1"; s->ycparts = 1; s->mcu_count *= 4; break;
}
/* Display information about the image */
fprintf(stderr, "Image ID: %02X\n", s->image_id);
fprintf(stderr, "Resolution: %ix%i\n", s->width, s->height);
fprintf(stderr, "MCU blocks: %i\n", s->mcu_count);
fprintf(stderr, "Sampling factor: %s\n", factor);
/* Output JPEG headers and enable byte stuffing */ /* Output JPEG headers and enable byte stuffing */
ssdv_out_headers(s); ssdv_out_headers(s);
@ -1160,9 +1199,12 @@ void ssdv_dec_header(ssdv_packet_info_t *info, uint8_t *packet)
info->packet_id = (packet[3] << 8) | packet[4]; info->packet_id = (packet[3] << 8) | packet[4];
info->width = packet[5] << 4; info->width = packet[5] << 4;
info->height = packet[6] << 4; info->height = packet[6] << 4;
info->mcu_offset = (packet[7] << 8) | packet[8]; info->mcu_mode = packet[7] & 0x03;
info->mcu_id = (packet[9] << 8) | packet[10]; info->mcu_offset = (packet[8] << 8) | packet[9];
info->mcu_id = (packet[10] << 8) | packet[11];
info->mcu_count = packet[5] * packet[6]; info->mcu_count = packet[5] * packet[6];
if(info->mcu_mode == 1 || info->mcu_mode == 2) info->mcu_count *= 2;
else if(info->mcu_mode == 3) info->mcu_count *= 4;
} }
/*****************************************************************************/ /*****************************************************************************/

7
ssdv.h
View File

@ -1,7 +1,7 @@
/* SSDV - Slow Scan Digital Video */ /* SSDV - Slow Scan Digital Video */
/*=======================================================================*/ /*=======================================================================*/
/* Copyright 2011 Philip Heron <phil@sanslogic.co.uk */ /* Copyright 2011-2012 Philip Heron <phil@sanslogic.co.uk */
/* */ /* */
/* This program is free software: you can redistribute it and/or modify */ /* This program is free software: you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */ /* it under the terms of the GNU General Public License as published by */
@ -33,7 +33,7 @@ extern "C" {
/* Packet details */ /* Packet details */
#define SSDV_PKT_SIZE (0x100) #define SSDV_PKT_SIZE (0x100)
#define SSDV_PKT_SIZE_HEADER (0x0B) #define SSDV_PKT_SIZE_HEADER (0x0C)
#define SSDV_PKT_SIZE_CRC (0x02) #define SSDV_PKT_SIZE_CRC (0x02)
#define SSDV_PKT_SIZE_RSCODES (0x20) #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 SSDV_PKT_SIZE_PAYLOAD (SSDV_PKT_SIZE - SSDV_PKT_SIZE_HEADER - SSDV_PKT_SIZE_CRC - SSDV_PKT_SIZE_RSCODES)
@ -48,6 +48,7 @@ typedef struct
uint16_t height; uint16_t height;
uint8_t image_id; uint8_t image_id;
uint16_t packet_id; uint16_t packet_id;
uint8_t mcu_mode; /* 0 = 2x2, 1 = 2x1, 2 = 1x2, 3 = 1x1 */
uint16_t mcu_id; uint16_t mcu_id;
uint16_t mcu_count; uint16_t mcu_count;
uint16_t packet_mcu_id; uint16_t packet_mcu_id;
@ -86,6 +87,7 @@ typedef struct
uint8_t *marker_data; /* Where to copy marker data too */ uint8_t *marker_data; /* Where to copy marker data too */
uint16_t marker_data_len; /* How much is there */ uint16_t marker_data_len; /* How much is there */
uint8_t component; /* 0 = Y, 1 = Cb, 2 = Cr */ 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 mcupart; /* 0-3 = Y, 4 = Cb, 5 = Cr */
uint8_t acpart; /* 0 - 64; 0 = DC, 1 - 64 = AC */ uint8_t acpart; /* 0 - 64; 0 = DC, 1 - 64 = AC */
int dc[COMPONENTS]; /* DC value for each component */ int dc[COMPONENTS]; /* DC value for each component */
@ -110,6 +112,7 @@ typedef struct {
uint16_t packet_id; uint16_t packet_id;
uint16_t width; uint16_t width;
uint16_t height; uint16_t height;
uint16_t mcu_mode;
uint16_t mcu_offset; uint16_t mcu_offset;
uint16_t mcu_id; uint16_t mcu_id;
uint16_t mcu_count; uint16_t mcu_count;