Add support for multiple subsampling formats. Not backwards compatible!
This commit is contained in:
parent
604f4f17f5
commit
2df18c50a1
1
README
1
README
|
@ -15,7 +15,6 @@ TODO
|
|||
|
||||
* Allow the decoder to handle multiple images in the input stream.
|
||||
* Experiment with adaptive or multiple huffman tables.
|
||||
* Support more than one subsampling configuration. (4 modes / 2 bits)
|
||||
* Quality setting (4 bit / 16 quality levels).
|
||||
* Add a callsign field to each packet.
|
||||
|
||||
|
|
100
ssdv.c
100
ssdv.c
|
@ -1,7 +1,7 @@
|
|||
|
||||
/* 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 */
|
||||
/* 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)
|
||||
{
|
||||
/* 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]);
|
||||
else
|
||||
|
@ -376,7 +376,7 @@ static char ssdv_process(ssdv_t *s)
|
|||
|
||||
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)
|
||||
{
|
||||
|
@ -450,7 +450,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++;
|
||||
|
@ -480,8 +480,8 @@ static char ssdv_process(ssdv_t *s)
|
|||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -574,14 +574,10 @@ 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);
|
||||
|
||||
/* Display information about the image... */
|
||||
fprintf(stderr, "Precision: %i\n", d[0]);
|
||||
fprintf(stderr, "Resolution: %ix%i\n", s->width, s->height);
|
||||
fprintf(stderr, "Components: %i\n", d[5]);
|
||||
fprintf(stderr, "MCU blocks: %i\n", s->mcu_count);
|
||||
|
||||
/* The image must have a precision of 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);
|
||||
|
||||
/* The first component must have a factor of 2x2 and the rest 1x1 */
|
||||
if(dq[0] == 1 && dq[1] != 0x22)
|
||||
/* The first (Y) component must have a factor of 2x2,2x1,1x2 or 1x1 */
|
||||
if(dq[0] == 1)
|
||||
{
|
||||
fprintf(stderr, "Error: Component 1 sampling factor must be 2x2\n");
|
||||
return(SSDV_ERROR);
|
||||
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:
|
||||
fprintf(stderr, "Error: Component 1 sampling factor is not supported\n");
|
||||
return(SSDV_ERROR);
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
||||
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[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[7] = s->mcu_mode & 0x03; /* MCU mode (2 bits) */
|
||||
s->out[8] = mcu_offset >> 8; /* Next MCU offset MSB */
|
||||
s->out[9] = mcu_offset & 0xFF; /* Next MCU offset 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 */
|
||||
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[5] = 3; /* Components (Y'Cb'Cr) */
|
||||
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[9] = 2; /* Cb */
|
||||
b[10] = 0x11;
|
||||
|
@ -946,10 +968,10 @@ static void ssdv_fill_gap(ssdv_t *s, uint16_t next_mcu)
|
|||
}
|
||||
|
||||
/* 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;
|
||||
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; ssdv_out_jpeg_int(s, 0, 0); /* DC */
|
||||
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++)
|
||||
{
|
||||
/* 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;
|
||||
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; ssdv_out_jpeg_int(s, 0, 0); /* DC */
|
||||
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 */
|
||||
packet_id = (packet[3] << 8) | packet[4];
|
||||
s->packet_mcu_offset = (packet[7] << 8) | packet[8];
|
||||
s->packet_mcu_id = (packet[9] << 8) | packet[10];
|
||||
s->packet_mcu_offset = (packet[8] << 8) | packet[9];
|
||||
s->packet_mcu_id = (packet[10] << 8) | packet[11];
|
||||
|
||||
if(s->packet_mcu_id != 0xFFFF) s->reset_mcu = s->packet_mcu_id;
|
||||
|
||||
/* If this is the first packet, write the JPEG headers */
|
||||
if(s->packet_id == 0)
|
||||
{
|
||||
const char *factor;
|
||||
|
||||
/* Read the fixed headers from the packet */
|
||||
s->image_id = packet[2];
|
||||
s->width = packet[5] << 4;
|
||||
s->height = packet[6] << 4;
|
||||
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 */
|
||||
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->width = packet[5] << 4;
|
||||
info->height = packet[6] << 4;
|
||||
info->mcu_offset = (packet[7] << 8) | packet[8];
|
||||
info->mcu_id = (packet[9] << 8) | packet[10];
|
||||
info->mcu_mode = packet[7] & 0x03;
|
||||
info->mcu_offset = (packet[8] << 8) | packet[9];
|
||||
info->mcu_id = (packet[10] << 8) | packet[11];
|
||||
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
7
ssdv.h
|
@ -1,7 +1,7 @@
|
|||
|
||||
/* 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 */
|
||||
/* it under the terms of the GNU General Public License as published by */
|
||||
|
@ -33,7 +33,7 @@ extern "C" {
|
|||
|
||||
/* Packet details */
|
||||
#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_RSCODES (0x20)
|
||||
#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;
|
||||
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;
|
||||
|
@ -86,6 +87,7 @@ typedef struct
|
|||
uint8_t *marker_data; /* Where to copy marker data too */
|
||||
uint16_t marker_data_len; /* How much is there */
|
||||
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[COMPONENTS]; /* DC value for each component */
|
||||
|
@ -110,6 +112,7 @@ typedef struct {
|
|||
uint16_t packet_id;
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
uint16_t mcu_mode;
|
||||
uint16_t mcu_offset;
|
||||
uint16_t mcu_id;
|
||||
uint16_t mcu_count;
|
||||
|
|
Loading…
Reference in New Issue