概述:在使用libjpeg在我的项目中,在测试过程中压缩图像出错,然后程序直接退出了,这对于我们程序来说那肯定是不行的啊。所以就开始找问题啦,在网上找到原因,原来是我们在压缩过程中使用了libjpeg默认的出错处理函数error_exit(),所以导致出错以后程序就整个退出了。今天我主要就是把这个分析的过程记录下来。
我们先看一个例子,我们使用默认的错误处理函数进行图片解压缩。代码如下:
#include
#include
#include
/*读取jpg文件并解压,使用官方自带的error处理函数*/
unsigned char *read_jpeg_file(char *filename)
{
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
FILE *infile;
JSAMPARRAY buffer;
int row_stride;
if (NULL == (infile = fopen(filename, "rb"))) {
printf("can't open %s\n",filename);
return NULL;
}
cinfo.err = jpeg_std_error(&jerr);
/*初始化*/
jpeg_create_decompress(&cinfo);
/*指定输入文件*/
jpeg_stdio_src(&cinfo, infile);
/*读取文件信息*/
jpeg_read_header(&cinfo, TRUE);
/*解压*/
jpeg_start_decompress(&cinfo);
row_stride = cinfo.output_height * cinfo.output_width * cinfo.output_components;
/*输出图像信息*/
printf("output_width = %d\n", cinfo.output_width);
printf("output_height = %d\n", cinfo.output_height);
printf("output_components = %d\n", cinfo.output_components);
/*申请内存用于存储解压数据*/
unsigned char *data = (unsigned char *)malloc(sizeof(unsigned char) * row_stride);
/*逐行扫描获取解压信息*/
JSAMPROW row_pointer[1];
while (cinfo.output_scanline < cinfo.output_height) {
row_pointer[0] = &data[(cinfo.output_height - cinfo.output_scanline-1)*cinfo.output_width*cinfo.output_components];
jpeg_read_scanlines(&cinfo,row_pointer ,1);
}
/*完成解压*/
jpeg_finish_decompress(&cinfo);
/*销毁解压cinfo信息*/
jpeg_destroy_decompress(&cinfo);
fclose(infile);
return data;
}
int main(int argc, char *argv[])
{
if (argc != 2) {
printf("Please input decompress img path, ep:./jpeg_error_test.c ./test.jpg\n");
return -1;
}
printf("decompress img: %s\n",argv[1]);
unsigned char *decompress_img = read_jpeg_file(argv[1]);
if (NULL == decompress_img) {
printf("decompress failed\n");
} else {
printf("decompress success\n");
free(decompress_img);
decompress_img = NULL;
}
/*死循环,让程序一直运行*/
while (1) {
sleep(1);
}
return 0;
}
我们看一下正确的jpg图片,解压缩执行结果:
程序解压正确,程序一直运行,未退出,我们复制这张图片,用vim打开我们的图片,随便删除一些内容,损坏这张图片,我们再运行看一下结果:
程序直接退出了,那我们看一下libjpeg源码,分析一下为何会直接退出呢。
在我们例子中用了以下的代码:
/*错误处理函数*/
struct jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
首先看一下,jpeg_std_error()这个函数(在jerror.c文件),这个函数其实就是初始化的作用。最主要的就是error_exit.
接下来我们看一下,libjpeg中默认的error_exit函数。
从上面的error_exit函数我们知道了,为何程序会退出,就在最后一句啦。exit(EXIT_FAILURE);
那这个函数何时调用的呢,我们在jerror.h中看到如下宏定义:
我们搜索ERREXIT关键字,发现在我们解压,压缩这样调用的函数,源代码中出错时都会调用上图中的函数,所以一出现错误,程序就会退出。
那如何解决这样的问题呢,其实在libjpeg源码中,examaple.c中就已经解决了这个问题,怪自己没好好看注释啊,主要还是英文的,我这要好好学习英语啦。那我们看一下这个例子吧,同样是解压缩的例子,和上面例子唯一的区别就是,我们用自己的错误处理函数,代替了原有的错误处理函数error_exit.
例子代码如下:
#include
#include
#include
#include
struct skyjpeg_error_mgr {
struct jpeg_error_mgr pub;
jmp_buf setjmp_buffer;
};
typedef struct skyjpeg_error_mgr *skyjpeg_error_ptr;
void skyjpeg_error_exit (j_common_ptr cinfo)
{
char skyjpeg_error_msg[JMSG_LENGTH_MAX];
/* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
skyjpeg_error_ptr myerr = (skyjpeg_error_ptr) cinfo->err;
/* Always display the message. */
/*这是官方例子里面的,打印出错误信息,我们就不用这个,我们让它把错误信息放到一个数组里面*/
/*(*cinfo->err->output_message) (cinfo);*/
/*错误信息放到数组*/
(*cinfo->err->format_message) (cinfo, skyjpeg_error_msg);
/*打印错误信息*/
printf("\n error msg:\n %s\n",skyjpeg_error_msg);
/* Return control to the setjmp point */
longjmp(myerr->setjmp_buffer, 1);
}
unsigned char *read_jpeg_file(char *filename)
{
struct jpeg_decompress_struct cinfo;
struct skyjpeg_error_mgr jerr;
FILE *infile;
JSAMPARRAY buffer;
int row_stride;
if (NULL == (infile = fopen(filename, "rb"))) {
printf("can't open %s\n",filename);
return NULL;
}
cinfo.err = jpeg_std_error(&jerr.pub);
/*用我们定义的skyjpeg_error_exit函数覆盖libjpeg默认的error_exit函数*/
jerr.pub.error_exit = skyjpeg_error_exit;
if (setjmp(jerr.setjmp_buffer)) {
jpeg_destroy_decompress(&cinfo);
fclose(infile);
return NULL;
}
/*初始化*/
jpeg_create_decompress(&cinfo);
/*指定输入文件*/
jpeg_stdio_src(&cinfo, infile);
/*读取文件信息*/
jpeg_read_header(&cinfo, TRUE);
/*解压*/
jpeg_start_decompress(&cinfo);
row_stride = cinfo.output_height * cinfo.output_width * cinfo.output_components;
/*输出图像信息*/
printf("output_width = %d\n", cinfo.output_width);
printf("output_height = %d\n", cinfo.output_height);
printf("output_components = %d\n", cinfo.output_components);
/*申请内存用于存储解压数据*/
unsigned char *data = (unsigned char *)malloc(sizeof(unsigned char) * row_stride);
/*逐行扫描获取解压信息*/
JSAMPROW row_pointer[1];
while (cinfo.output_scanline < cinfo.output_height) {
row_pointer[0] = &data[(cinfo.output_height - cinfo.output_scanline-1)*cinfo.output_width*cinfo.output_components];
jpeg_read_scanlines(&cinfo,row_pointer ,1);
}
/*完成解压*/
jpeg_finish_decompress(&cinfo);
/*销毁解压cinfo信息*/
jpeg_destroy_decompress(&cinfo);
fclose(infile);
return data;
}
int main(int argc, char *argv[])
{
if (argc != 2) {
printf("Please input decompress img path, ep:./jpeg_error_test.c ./test.jpg\n");
return -1;
}
printf("decompress img: %s\n",argv[1]);
unsigned char *decompress_img = read_jpeg_file(argv[1]);
if (NULL == decompress_img) {
printf("decompress failed\n");
} else {
printf("decompress success\n");
free(decompress_img);
decompress_img = NULL;
}
/*死循环,让程序一直运行*/
while (1) {
sleep(1);
}
return 0;
}
我们编译完成后,用这个代码执行对已经损坏的jpg文件,解压。我们看看程序是不是还会退出呢。当然,程序没有退出啦,结果如下:
其实这里主要用到的就是下面代码,用自己的错误处理函数,覆盖默认的函数。
/*自定义一个错误处理的结构体*/
struct skyjpeg_error_mgr {
struct jpeg_error_mgr pub;
jmp_buf setjmp_buffer;
};
typedef struct skyjpeg_error_mgr *skyjpeg_error_ptr;
/*自己的错误处理函数*/
void skyjpeg_error_exit (j_common_ptr cinfo)
{
char skyjpeg_error_msg[JMSG_LENGTH_MAX];
/* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
skyjpeg_error_ptr myerr = (skyjpeg_error_ptr) cinfo->err;
/* Always display the message. */
/*这是官方例子里面的,打印出错误信息,我们就不用这个,我们让它把错误信息放到一个数组里面*/
/*(*cinfo->err->output_message) (cinfo);*/
/*错误信息放到数组*/
(*cinfo->err->format_message) (cinfo, skyjpeg_error_msg);
/*打印错误信息*/
printf("\n error msg:\n %s\n",skyjpeg_error_msg);
/* Return control to the setjmp point */
longjmp(myerr->setjmp_buffer, 1);
}
cinfo.err = jpeg_std_error(&jerr.pub);
/*用我们定义的skyjpeg_error_exit函数覆盖libjpeg默认的error_exit函数*/
jerr.pub.error_exit = skyjpeg_error_exit;
if (setjmp(jerr.setjmp_buffer)) {
jpeg_destroy_decompress(&cinfo);
fclose(infile);
return NULL;
}
总结:
OK,问题解决。但是这些问题也是因为没有好好看例子所以导致了一些问题的出现,所以在使用第三方库时最好还是好好看看例子及注释,避免一些不必要的错误。Peace&Love.