kopia lustrzana https://github.com/jamescoxon/dl-fldigi
668 wiersze
15 KiB
C++
668 wiersze
15 KiB
C++
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <stdint.h>
|
|
#include <setjmp.h>
|
|
#include "ssdv_rx.h"
|
|
#include "timeops.h"
|
|
|
|
#include <curl/curl.h>
|
|
|
|
/* For put_status() */
|
|
#include "fl_digi.h"
|
|
|
|
/* For progdefaults */
|
|
#include "configuration.h"
|
|
|
|
/* For online() getter */
|
|
#include "dl_fldigi/dl_fldigi.h"
|
|
|
|
/* Used for passing curl data to post thread */
|
|
typedef struct {
|
|
CURL *curl;
|
|
struct curl_httppost* post;
|
|
} ssdv_post_data_t;
|
|
|
|
#if 1
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
#include <jpeglib.h>
|
|
#include <jerror.h>
|
|
|
|
/* JPEG MEMORY SOURCE */
|
|
|
|
typedef struct {
|
|
struct jpeg_source_mgr pub; /* public fields */
|
|
} my_source_mgr;
|
|
|
|
METHODDEF(void)
|
|
init_source (j_decompress_ptr cinfo)
|
|
{
|
|
/* no work necessary here */
|
|
}
|
|
|
|
METHODDEF(boolean)
|
|
fill_input_buffer (j_decompress_ptr cinfo)
|
|
{
|
|
my_source_mgr *src = (my_source_mgr *) cinfo->src;
|
|
static JOCTET fakeeoi[4];
|
|
|
|
WARNMS(cinfo, JWRN_JPEG_EOF);
|
|
|
|
/* Insert a fake EOI marker */
|
|
fakeeoi[0] = (JOCTET) 0xFF;
|
|
fakeeoi[1] = (JOCTET) JPEG_EOI;
|
|
|
|
src->pub.next_input_byte = fakeeoi;
|
|
src->pub.bytes_in_buffer = 2;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
METHODDEF(void)
|
|
skip_input_data (j_decompress_ptr cinfo, long num_bytes)
|
|
{
|
|
my_source_mgr *src = (my_source_mgr *) cinfo->src;
|
|
|
|
if (num_bytes > 0) {
|
|
while (num_bytes > (long) src->pub.bytes_in_buffer) {
|
|
num_bytes -= (long) src->pub.bytes_in_buffer;
|
|
(void) fill_input_buffer(cinfo);
|
|
/* note we assume that fill_input_buffer will never return FALSE,
|
|
* so suspension need not be handled.
|
|
*/
|
|
}
|
|
src->pub.next_input_byte += (size_t) num_bytes;
|
|
src->pub.bytes_in_buffer -= (size_t) num_bytes;
|
|
}
|
|
}
|
|
|
|
METHODDEF(void)
|
|
term_source (j_decompress_ptr cinfo)
|
|
{
|
|
/* no work necessary here */
|
|
}
|
|
|
|
GLOBAL(void)
|
|
jpeg_memory_src(j_decompress_ptr cinfo, unsigned char *inbuffer, size_t insize)
|
|
{
|
|
my_source_mgr *src;
|
|
|
|
if(inbuffer == NULL || insize == 0) /* Treat empty input as fatal error */
|
|
ERREXIT(cinfo, JERR_INPUT_EMPTY);
|
|
|
|
/* The source object is made permanent so that a series of JPEG images
|
|
* can be read from a single buffer by calling jpeg_memory_src
|
|
* only before the first one.
|
|
*/
|
|
if(cinfo->src == NULL) { /* first time for this JPEG object? */
|
|
cinfo->src = (struct jpeg_source_mgr *) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(my_source_mgr));
|
|
}
|
|
|
|
src = (my_source_mgr *) cinfo->src;
|
|
src->pub.init_source = init_source;
|
|
src->pub.fill_input_buffer = fill_input_buffer;
|
|
src->pub.skip_input_data = skip_input_data;
|
|
src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
|
|
src->pub.term_source = term_source;
|
|
|
|
src->pub.next_input_byte = inbuffer;
|
|
src->pub.bytes_in_buffer = insize;
|
|
}
|
|
|
|
/**** JPEG ERROR HANDLER ****/
|
|
struct ssdv_error_mgr {
|
|
struct jpeg_error_mgr pub; /* "public" fields */
|
|
jmp_buf setjmp_buffer; /* for return to caller */
|
|
};
|
|
|
|
typedef struct ssdv_error_mgr* ssdv_error_ptr;
|
|
|
|
METHODDEF(void)
|
|
ssdv_error_exit (j_common_ptr cinfo)
|
|
{
|
|
ssdv_error_ptr myerr = (ssdv_error_ptr) cinfo->err;
|
|
longjmp(myerr->setjmp_buffer, 1);
|
|
}
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif
|
|
|
|
/**** END JPEG STUFF *****/
|
|
|
|
#define UI_HEIGHT (60)
|
|
#define WIN_MIN_WIDTH (320)
|
|
#define WIN_MIN_HEIGHT (32 + UI_HEIGHT)
|
|
#define WIN_MAX_WIDTH (800)
|
|
#define WIN_MAX_HEIGHT (600 + UI_HEIGHT)
|
|
|
|
ssdv_rx::ssdv_rx(int w, int h, const char *title)
|
|
: Fl_Double_Window(w, h, title)
|
|
{
|
|
buffer = new uint8_t[BUFFER_SIZE];
|
|
erasures = new uint8_t[BUFFER_SIZE];
|
|
|
|
/* Empty receive buffer */
|
|
clear_buffer();
|
|
|
|
/* No image yet */
|
|
packets = NULL;
|
|
image = NULL;
|
|
flrgb = NULL;
|
|
image_id = -1;
|
|
|
|
begin();
|
|
|
|
scroll = new Fl_Scroll(0, 0, w, h - UI_HEIGHT);
|
|
scroll->begin();
|
|
box = new Fl_Box(0, 0, w, h - UI_HEIGHT);
|
|
box->box(FL_FLAT_BOX);
|
|
box->color(fl_rgb_color(0x80, 0x80, 0x80));
|
|
scroll->end();
|
|
|
|
int y = h - UI_HEIGHT;
|
|
int x1 = 0;
|
|
int x2 = x1 + 75;
|
|
int x3 = x2 + 100;
|
|
|
|
/* Current Image Callsign: */
|
|
{
|
|
Fl_Box* o = new Fl_Box(x1 + 2, y + 2, 72, 20, "Callsign:");
|
|
o->align(FL_ALIGN_RIGHT | FL_ALIGN_INSIDE);
|
|
|
|
flcallsign = new Fl_Box(x1 + 71, y + 2, 60, 20, "");
|
|
flcallsign->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE);
|
|
}
|
|
|
|
/* Current Image ID: */
|
|
{
|
|
Fl_Box* o = new Fl_Box(x2 + 2, y + 2, 72, 20, "ID:");
|
|
o->align(FL_ALIGN_RIGHT | FL_ALIGN_INSIDE);
|
|
|
|
flimageid = new Fl_Box(x2 + 71, y + 2, 60, 20, "");
|
|
flimageid->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE);
|
|
}
|
|
|
|
/* Received: */
|
|
{
|
|
Fl_Box* o = new Fl_Box(x1 + 2, y + 22, 72, 20, "Received:");
|
|
o->align(FL_ALIGN_RIGHT | FL_ALIGN_INSIDE);
|
|
|
|
flreceived = new Fl_Box(x1 + 71, y + 22, 60, 20, "");
|
|
flreceived->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE);
|
|
}
|
|
|
|
/* Size: */
|
|
{
|
|
Fl_Box* o = new Fl_Box(x3 + 2, y + 2, 72, 20, "Size:");
|
|
o->align(FL_ALIGN_RIGHT | FL_ALIGN_INSIDE);
|
|
|
|
flsize = new Fl_Box(x3 + 71, y + 2, 60, 20, "");
|
|
flsize->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE);
|
|
}
|
|
|
|
/* Fixes: */
|
|
{
|
|
Fl_Box* o = new Fl_Box(x2 + 2, y + 22, 72, 20, "Fixes:");
|
|
o->align(FL_ALIGN_RIGHT | FL_ALIGN_INSIDE);
|
|
|
|
flfixes = new Fl_Box(x2 + 71, y + 22, 60, 20, "");
|
|
flfixes->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE);
|
|
}
|
|
|
|
/* Lost: */
|
|
{
|
|
Fl_Box* o = new Fl_Box(x3 + 2, y + 22, 72, 20, "Lost:");
|
|
o->align(FL_ALIGN_RIGHT | FL_ALIGN_INSIDE);
|
|
|
|
flmissing = new Fl_Box(x3 + 71, y + 22, 60, 20, "");
|
|
flmissing->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE);
|
|
}
|
|
|
|
/* Progress Bar */
|
|
flprogress = new Fl_Progress(0, h - 12, w, 12);
|
|
flprogress->minimum(0);
|
|
flprogress->maximum(1);
|
|
flprogress->value(0);
|
|
|
|
end();
|
|
|
|
size_range(WIN_MIN_WIDTH, WIN_MIN_HEIGHT, 0, 0, 0, 0, 0);
|
|
resizable(scroll);
|
|
}
|
|
|
|
ssdv_rx::~ssdv_rx()
|
|
{
|
|
if(flrgb) delete flrgb;
|
|
if(image) delete image;
|
|
if(buffer) delete buffer;
|
|
if(erasures) delete erasures;
|
|
}
|
|
|
|
void ssdv_rx::feed_buffer(uint8_t byte, uint8_t erasure)
|
|
{
|
|
int bp = bc + bl;
|
|
|
|
buffer[bp] = byte;
|
|
erasures[bp] = (erasure ? 1 : 0);
|
|
if((bp -= SSDV_PKT_SIZE) >= 0)
|
|
{
|
|
buffer[bp] = byte;
|
|
erasures[bp] = (erasure ? 1 : 0);
|
|
}
|
|
|
|
if(bl < SSDV_PKT_SIZE) bl++;
|
|
else if(++bc == SSDV_PKT_SIZE) bc = 0;
|
|
}
|
|
|
|
void ssdv_rx::clear_buffer()
|
|
{
|
|
bc = 0;
|
|
bl = 0;
|
|
}
|
|
|
|
static void *upload_packet_thread(void *arg)
|
|
{
|
|
ssdv_post_data_t *t = (ssdv_post_data_t *) arg;
|
|
struct curl_httppost* post = t->post;
|
|
CURL *curl = t->curl;
|
|
CURLcode r;
|
|
|
|
r = curl_easy_perform(curl);
|
|
if(r == 0)
|
|
{
|
|
fprintf(stderr, "SSDV: packet uploaded!\n");
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "CURL upload failed! \"%s\"\n", curl_easy_strerror(r));
|
|
}
|
|
|
|
curl_easy_cleanup(curl);
|
|
curl_formfree(post);
|
|
|
|
free(t);
|
|
|
|
pthread_exit(0);
|
|
}
|
|
|
|
/* TODO: HABITAT-LATER upload using habitat */
|
|
void ssdv_rx::upload_packet(int fixes)
|
|
{
|
|
ssdv_post_data_t *t;
|
|
pthread_attr_t attr;
|
|
pthread_t thread;
|
|
const char *callsign;
|
|
char *packet;
|
|
char data[10];
|
|
struct curl_httppost* post = NULL;
|
|
struct curl_httppost* last = NULL;
|
|
CURL *curl;
|
|
|
|
/* Don't upload if no URL is present */
|
|
if(progdefaults.ssdv_packet_url.length() <= 0) return;
|
|
|
|
/* Get the callsign, or "UNKNOWN" if none is set */
|
|
callsign = (progdefaults.myCall.empty() ? "UNKNOWN" : progdefaults.myCall.c_str());
|
|
curl_formadd(&post, &last, CURLFORM_COPYNAME, "callsign",
|
|
CURLFORM_COPYCONTENTS, callsign, CURLFORM_END);
|
|
|
|
/* The encoding used on the packet */
|
|
curl_formadd(&post, &last, CURLFORM_COPYNAME, "encoding",
|
|
CURLFORM_COPYCONTENTS, "hex", CURLFORM_END);
|
|
|
|
/* Include the number of bytes corrected by the FEC */
|
|
snprintf(data, 10, "%i", fixes);
|
|
curl_formadd(&post, &last, CURLFORM_COPYNAME, "fixes",
|
|
CURLFORM_COPYCONTENTS, data, CURLFORM_END);
|
|
|
|
/* URL-encode the packet */
|
|
packet = (char *) malloc((SSDV_PKT_SIZE * 2) + 1);
|
|
if(!packet)
|
|
{
|
|
fprintf(stderr, "ssdv_rx::upload_packet(): failed to allocate memory for packet\n");
|
|
curl_formfree(post);
|
|
return;
|
|
}
|
|
|
|
for(int i = 0; i < SSDV_PKT_SIZE; i++)
|
|
snprintf(packet + (i * 2), 3, "%02X", buffer[bc + i]);
|
|
|
|
/* Add a copy of the packet */
|
|
curl_formadd(&post, &last, CURLFORM_COPYNAME, "packet",
|
|
CURLFORM_COPYCONTENTS, packet, CURLFORM_END);
|
|
|
|
free(packet);
|
|
|
|
/* Initialise libcurl */
|
|
curl = curl_easy_init();
|
|
if(!curl)
|
|
{
|
|
fprintf(stderr, "ssdv_rx::upload_packet(): curl_easy_init() failed\n");
|
|
curl_formfree(post);
|
|
return;
|
|
}
|
|
|
|
curl_easy_setopt(curl, CURLOPT_URL, progdefaults.ssdv_packet_url.c_str());
|
|
curl_easy_setopt(curl, CURLOPT_HTTPPOST, post);
|
|
|
|
/* Begin the thread to do the post */
|
|
t = (ssdv_post_data_t *) malloc(sizeof(ssdv_post_data_t));
|
|
if(!t)
|
|
{
|
|
fprintf(stderr, "ssdv_rx::upload_packet(): failed to allocate memory for thread data\n");
|
|
curl_easy_cleanup(curl);
|
|
curl_formfree(post);
|
|
return;
|
|
}
|
|
|
|
t->curl = curl;
|
|
t->post = post;
|
|
|
|
pthread_attr_init(&attr);
|
|
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
|
|
|
if(pthread_create(&thread, &attr, upload_packet_thread, (void *) t) != 0)
|
|
{
|
|
fprintf(stderr, "ssdv_rx::upload_packet(): failed to start post thread\n");
|
|
curl_easy_cleanup(curl);
|
|
curl_formfree(post);
|
|
free(t);
|
|
}
|
|
|
|
pthread_attr_destroy(&attr);
|
|
|
|
/* All done! */
|
|
|
|
return;
|
|
}
|
|
|
|
void ssdv_rx::put_byte(uint8_t byte, int lost)
|
|
{
|
|
int i;
|
|
|
|
/* If more than 32 bytes where lost clear the buffer */
|
|
if(lost > 32) clear_buffer();
|
|
|
|
/* Fill in the lost bytes */
|
|
for(i = 0; i < lost; i++)
|
|
feed_buffer(0x00, 1);
|
|
|
|
/* Feed the byte into the buffer */
|
|
feed_buffer(byte, 0);
|
|
|
|
/* Enough data yet to form a packet? */
|
|
if(bl < SSDV_PKT_SIZE) return;
|
|
|
|
/* Test if this is a packet and is valid */
|
|
uint8_t *b = &buffer[bc];
|
|
if(ssdv_dec_is_packet(b, &i, &erasures[bc]) != 0) return;
|
|
|
|
/* Make a note of the number of errors */
|
|
image_errors += i;
|
|
|
|
/* Packet received.. upload to server */
|
|
if (dl_fldigi::online()) upload_packet(i);
|
|
|
|
/* Read the header */
|
|
ssdv_dec_header(&pkt_info, b);
|
|
|
|
/* Does this belong to the same image? */
|
|
if(pkt_info.callsign != image_callsign ||
|
|
pkt_info.image_id != image_id ||
|
|
pkt_info.width != image_width ||
|
|
pkt_info.height != image_height ||
|
|
pkt_info.mcu_mode != image_mcu_mode)
|
|
{
|
|
/* Prepare the new image */
|
|
image_timestamp = time(NULL);
|
|
image_callsign = pkt_info.callsign;
|
|
image_id = pkt_info.image_id;
|
|
image_width = pkt_info.width;
|
|
image_height = pkt_info.height;
|
|
image_mcu_mode = pkt_info.mcu_mode;
|
|
image_lost_packets = 0;
|
|
image_errors = i;
|
|
|
|
/* Initialise and clear the image buffer */
|
|
if(image) delete image;
|
|
image_len = image_width * image_height * 3;
|
|
image = new unsigned char[image_len];
|
|
|
|
/* Create the Fl_RGB_Image object */
|
|
if(flrgb) delete flrgb;
|
|
flrgb = new Fl_RGB_Image(image, image_width, image_height, 3);
|
|
box->size(image_width, image_height);
|
|
box->image(flrgb);
|
|
|
|
/* Snap the window to the new image size */
|
|
size(
|
|
CLAMP(image_width, WIN_MIN_WIDTH, WIN_MAX_WIDTH),
|
|
CLAMP(image_height + UI_HEIGHT, WIN_MIN_HEIGHT, WIN_MAX_HEIGHT)
|
|
);
|
|
|
|
/* Clear the packet buffer */
|
|
if(packets != NULL) free(packets);
|
|
packets = NULL;
|
|
packets_len = 0;
|
|
}
|
|
|
|
/* Realloc packet buffer for new packet */
|
|
if(pkt_info.packet_id + 1 > packets_len)
|
|
{
|
|
size_t l = SSDV_PKT_SIZE * (pkt_info.packet_id + 1);
|
|
void *a = realloc(packets, l);
|
|
if(!a)
|
|
{
|
|
fprintf(stderr, "Error reallocating memory\n");
|
|
perror("realloc");
|
|
return;
|
|
}
|
|
|
|
packets = (uint8_t *) a;
|
|
|
|
size_t s = SSDV_PKT_SIZE * packets_len;
|
|
memset(packets + s, 0, l - s);
|
|
|
|
packets_len = pkt_info.packet_id + 1;
|
|
}
|
|
|
|
/* Copy it into place */
|
|
memcpy(packets + (pkt_info.packet_id * SSDV_PKT_SIZE), b, SSDV_PKT_SIZE);
|
|
|
|
/* Done with the receive buffer */
|
|
clear_buffer();
|
|
|
|
/* Display a message on the fldigi interface */
|
|
put_status("SSDV: Decoded image packet!", 10);
|
|
|
|
char msg[200], callsign[10];
|
|
snprintf(msg, 200, "Decoded image packet. Callsign: %s, Image ID: %02X, Resolution: %dx%d, Packet ID: %d",
|
|
ssdv_decode_callsign(callsign, pkt_info.callsign),
|
|
pkt_info.image_id,
|
|
pkt_info.width,
|
|
pkt_info.height,
|
|
pkt_info.packet_id);
|
|
|
|
if(bHAB)
|
|
{
|
|
habString->value(msg);
|
|
habString->color(FL_GREEN);
|
|
habString->damage(FL_DAMAGE_ALL);
|
|
}
|
|
|
|
ReceiveText->addstr("\n");
|
|
ReceiveText->addstr(msg, FTextBase::QSY);
|
|
ReceiveText->addstr("\n");
|
|
|
|
/* Initialise the decoder */
|
|
ssdv_t dec;
|
|
ssdv_dec_init(&dec);
|
|
|
|
image_received_packets = 0;
|
|
image_lost_packets = 0;
|
|
for(i = 0; i < packets_len; i++)
|
|
{
|
|
int r;
|
|
uint8_t *p = packets + (i * SSDV_PKT_SIZE);
|
|
if(p[0] != 0x55) { image_lost_packets++; continue; }
|
|
image_received_packets++;
|
|
r = ssdv_dec_feed(&dec, p);
|
|
if (SSDV_ERROR == r) image_lost_packets++;
|
|
}
|
|
|
|
/* Store the last decoded MCU, for the progress bar */
|
|
int mcu_id = dec.mcu_id;
|
|
|
|
/* Get the final image */
|
|
uint8_t *jpeg;
|
|
size_t length;
|
|
|
|
i = ssdv_dec_get_jpeg(&dec, &jpeg, &length);
|
|
|
|
/* Save the image to disk */
|
|
save_image(jpeg, length);
|
|
|
|
/* Render the image to screen */
|
|
render_image(jpeg, length);
|
|
flrgb->uncache();
|
|
box->redraw();
|
|
|
|
free(jpeg);
|
|
|
|
/* Update values on display */
|
|
char s[16];
|
|
|
|
ssdv_decode_callsign(s, image_callsign);
|
|
flcallsign->copy_label(s);
|
|
|
|
snprintf(s, 16, "%d", image_received_packets);
|
|
flreceived->copy_label(s);
|
|
|
|
snprintf(s, 16, "0x%02X", pkt_info.image_id);
|
|
flimageid->copy_label(s);
|
|
|
|
snprintf(s, 16, "%d", image_lost_packets);
|
|
flmissing->copy_label(s);
|
|
|
|
snprintf(s, 16, "%d byte%s", image_errors, (image_errors == 1 ? "" : "s"));
|
|
flfixes->copy_label(s);
|
|
|
|
snprintf(s, 16, "%ix%i", image_width, image_height);
|
|
flsize->copy_label(s);
|
|
|
|
flprogress->maximum(dec.mcu_count);
|
|
flprogress->value(mcu_id);
|
|
}
|
|
|
|
void ssdv_rx::save_image(uint8_t *jpeg, size_t length)
|
|
{
|
|
char fname[FILENAME_MAX];
|
|
char payload[10];
|
|
const char *savedir;
|
|
struct tm tm;
|
|
FILE *f;
|
|
uint16_t l;
|
|
|
|
/* Does the user want the image saved? */
|
|
if(!progdefaults.ssdv_save_image) return;
|
|
|
|
/* Save Directory - user specified or pwd */
|
|
savedir = (progdefaults.ssdv_save_dir.empty() ?
|
|
"." : progdefaults.ssdv_save_dir.c_str());
|
|
|
|
/* Get the payload name */
|
|
if(!image_callsign) strcpy(payload, "UNKNOWN");
|
|
ssdv_decode_callsign(payload, image_callsign);
|
|
|
|
/* Construct the filename */
|
|
gmtime_r(&image_timestamp, &tm);
|
|
snprintf(fname, FILENAME_MAX - 1,
|
|
"%s/%04i-%02i-%02i-%02i-%02i-%02i-%s-%02X.jpeg",
|
|
savedir,
|
|
1900 + tm.tm_year, 1 + tm.tm_mon, tm.tm_mday,
|
|
tm.tm_hour, tm.tm_min, tm.tm_sec,
|
|
payload, image_id);
|
|
|
|
f = fopen(fname, "wb");
|
|
if(!f)
|
|
{
|
|
fprintf(stderr, "Error saving image to %s\n", fname);
|
|
/* TODO: Error reporting -- is strerror thread safe? */
|
|
return;
|
|
}
|
|
|
|
/* Loop until all bytes are written */
|
|
l = 0;
|
|
while(l < length)
|
|
{
|
|
size_t r = fwrite(jpeg + l, 1, length - l, f);
|
|
if(r == 0) break;
|
|
l += r;
|
|
}
|
|
fclose(f);
|
|
|
|
/* Job done */
|
|
}
|
|
|
|
void ssdv_rx::render_image(uint8_t *jpeg, size_t length)
|
|
{
|
|
int r;
|
|
struct jpeg_decompress_struct cinfo;
|
|
struct ssdv_error_mgr jerr;
|
|
|
|
cinfo.err = jpeg_std_error(&jerr.pub);
|
|
jerr.pub.error_exit = ssdv_error_exit;
|
|
|
|
if(setjmp(jerr.setjmp_buffer))
|
|
{
|
|
/* JPEG decoding failed */
|
|
jpeg_destroy_decompress(&cinfo);
|
|
return;
|
|
}
|
|
|
|
jpeg_create_decompress(&cinfo);
|
|
jpeg_memory_src(&cinfo, jpeg, length);
|
|
|
|
r = jpeg_read_header(&cinfo, TRUE);
|
|
if(r != JPEG_HEADER_OK)
|
|
{
|
|
jpeg_destroy_decompress(&cinfo);
|
|
return;
|
|
}
|
|
|
|
int row_stride;
|
|
|
|
/* Force RGB output and use floating point DCT */
|
|
cinfo.out_color_space = JCS_RGB;
|
|
cinfo.dct_method = JDCT_FLOAT;
|
|
|
|
jpeg_start_decompress(&cinfo);
|
|
|
|
/* Fail if the image doesn't match our requirements */
|
|
if(cinfo.output_components != 3)
|
|
{
|
|
jpeg_finish_decompress(&cinfo);
|
|
jpeg_destroy_decompress(&cinfo);
|
|
return;
|
|
}
|
|
|
|
row_stride = cinfo.output_width * cinfo.output_components;
|
|
|
|
while(cinfo.output_scanline < cinfo.output_height)
|
|
{
|
|
uint8_t *b = &image[cinfo.output_scanline * row_stride];
|
|
jpeg_read_scanlines(&cinfo, &b, 1);
|
|
}
|
|
|
|
jpeg_finish_decompress(&cinfo);
|
|
jpeg_destroy_decompress(&cinfo);
|
|
}
|
|
|