当前位置: 首页 > GNU/Linux程序, 业余研究, 流媒体学习 > 正文

libjpeg学习2:内存篇

前面文章说到到libjpeg的使用示例,里面的例子实际上是文件的操作,即解压JPEG文件,因为libjpeg有对FILE操作的函数,所以代码直接用jpeg_stdio_src(&cinfo, fp);就行了,这个库会去读取JPEG文件。但是实际应用场合中,很多都不是文件,比如从网络传输过来的是JPEG数据,需要解压为RGB或YUV;又或者传输RGB数据要转换成JPEG。总之,是基于内存的操作的。往往用文件的方式来处理很简单,而换成了内存操作就有点措手不及了。正如上个月搞的FFMPEG转换,保存为一个视频文件是很简单的,但我要将转换后的数据保存到某一片内存中传输到上位机,我不可能先保存为文件再读文件,这太没职业道德了。幸运的是,我花了1周多的时间搞定了这个问题。

更幸运的是,libjpeg新版本已经实现了内存的读写。我依稀记得当年使用一个很旧的版本,里面是没有内存的接口的,因此上网找,竟然也能找到有人自己实现了内存接口。但后来发现新版本库就有了,于是我就不用画蛇添足了。

解压内存中的JPEG里,使用jpeg_mem_src函数,如jpeg_mem_src(&cinfo, jpeg_buffer, jpeg_size)。而要将内存中的RGB压缩成JPEG,则使用jpeg_mem_dest接口,如jpeg_mem_src(&cinfo, jpeg_buffer, jpeg_size)。注意,jpeg_buffer是二级指针,不用用户申请,由libjpeg申请空间,但要用户自行释放。

头文件声明如下:

/**
 * 利用libjpeg将缓冲区的JPEG转换成RGB 解压JPEG
 * 
 * @param[IN]  jpeg_buffer  JPEG图片缓冲区
 * @param[IN]  jpeg_size    JPEG图片缓冲区大小
 * @param[IN] rgb_buffer    RGB缓冲区
 * @param[IN/OUT] size      RGB缓冲区大小
 * @param[OUT] width        图片宽
 * @param[OUT] height       图片高
 *
 * @return 
 *         0:成功
 *         -1:打开文件失败
 * @note
 *         jpeg、rgb空间由调用者申请,size为输入输出参数,传入为rgb空间大小,转出为rgb实际大小
 */
int jpeg2rgb(unsigned char* jpeg_buffer, int jpeg_size, unsigned char* rgb_buffer,
int* size, int* width, int* height);

/**
 * 利用libjpeg将缓冲区的RGB转换成JPEG 压缩为JPEG
 * 
 * @param[IN]  rgb_buffer JPEG图片RGB数据
 * @param[IN]  width       图片宽
 * @param[IN]  height      图片高
 * @param[IN]  quality     图片质量
 * @param[OUT] jpeg_buffer JPEG缓冲区指针
 * @param[OUT] jpeg_size   JPEG缓冲区大小
 *
 * @return 
 *         0:成功
 *         -1:打开文件失败
 * @note
 *         jpeg_buffer为二级指针,无须调用者申请空间,由libjpeg申请,但调用者要自行释放
 */
int rgb2jpeg(unsigned char* rgb_buffer, int width, int height, int quality, 
unsigned char** jpeg_buffer, unsigned long* jpeg_size);

代码实现如下:

#include  <stdio.h>
#include  <stdlib.h>
#include  <string.h>
#include  <setjmp.h>
#include  <math.h>
#include  <sys/time.h>
#include  <time.h>

// jpeg库头文件必须放到stdio.h后面
#include "libjpeg/include/jpeglib.h"
#include "libjpeg/include/jerror.h"

typedef struct my_error_mgr * my_error_ptr;

void my_error_exit (j_common_ptr cinfo)
{
    my_error_ptr myerr = (my_error_ptr) cinfo->err;

    (*cinfo->err->output_message) (cinfo);

    longjmp(myerr->setjmp_buffer, 1);
}

int jpeg2rgb(unsigned char* jpeg_buffer, int jpeg_size, unsigned char* rgb_buffer,
int* size, int* width, int* height)
{
    struct jpeg_decompress_struct cinfo;
    struct my_error_mgr jerr;

    JSAMPARRAY buffer;
    int row_stride = 0;
    unsigned char* tmp_buffer = NULL;
    int rgb_size;

    if (jpeg_buffer == NULL)
    {
        printf("no jpeg buffer here.\n");
        return -1;
    }
    if (rgb_buffer == NULL)
    {
        printf("you need to alloc rgb buffer.\n");
        return -1;
    }
    
    cinfo.err = jpeg_std_error(&jerr.pub);
    jerr.pub.error_exit = my_error_exit;

    if (setjmp(jerr.setjmp_buffer))
    {
        jpeg_destroy_decompress(&cinfo);
        return -1;
    }

    jpeg_create_decompress(&cinfo);

    jpeg_mem_src(&cinfo, jpeg_buffer, jpeg_size);

    jpeg_read_header(&cinfo, TRUE);

    //cinfo.out_color_space = JCS_RGB; //JCS_YCbCr;  // 设置输出格式

    jpeg_start_decompress(&cinfo);

    row_stride = cinfo.output_width * cinfo.output_components;
    *width = cinfo.output_width;
    *height = cinfo.output_height;

    rgb_size = row_stride * cinfo.output_height; // 总大小
    if (*size < rgb_size)
    {
        printf("rgb buffer to small, we need %d but has only: %d\n", rgb_size, *size);
    }
    
    *size = rgb_size;

    buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);

    printf("debug--:\nrgb_size: %d, size: %d w: %d h: %d row_stride: %d \n", rgb_size,
                cinfo.image_width*cinfo.image_height*3,
                cinfo.image_width, 
                cinfo.image_height,
                row_stride);
    tmp_buffer = rgb_buffer;
    while (cinfo.output_scanline < cinfo.output_height) // 解压每一行
    {
        jpeg_read_scanlines(&cinfo, buffer, 1);
        // 复制到内存
        memcpy(tmp_buffer, buffer[0], row_stride);
        tmp_buffer += row_stride;
    }

    jpeg_finish_decompress(&cinfo);
    jpeg_destroy_decompress(&cinfo);

    return 0;
}

int rgb2jpeg(unsigned char* rgb_buffer, int width, int height, int quality, 
unsigned char** jpeg_buffer, unsigned long* jpeg_size)
{
    struct jpeg_compress_struct cinfo;
    struct jpeg_error_mgr jerr;
    int row_stride = 0;
    JSAMPROW row_pointer[1];

    if (jpeg_buffer == NULL)
    {
        printf("you need a pointer for jpeg buffer.\n");
        return -1;
    }
    
    cinfo.err = jpeg_std_error(&jerr);

    jpeg_create_compress(&cinfo);

    jpeg_mem_dest(&cinfo, jpeg_buffer, jpeg_size);

    cinfo.image_width = width;
    cinfo.image_height = height;
    cinfo.input_components = 3;
    cinfo.in_color_space = JCS_RGB;

    jpeg_set_defaults(&cinfo);
    jpeg_set_quality(&cinfo, quality, 1);  // todo 1 == true
    jpeg_start_compress(&cinfo, TRUE);
    row_stride = width * cinfo.input_components;

    while (cinfo.next_scanline < cinfo.image_height)
    {
        row_pointer[0] = &rgb_buffer[cinfo.next_scanline * row_stride];
        jpeg_write_scanlines(&cinfo, row_pointer, 1);
    }

    jpeg_finish_compress(&cinfo);
    jpeg_destroy_compress(&cinfo);

    return 0;
}

个别参数还有优化地方,等有空了再深入研究一下。

时隔多年,又碰到了libjpeg加速版本,名为turbo-libjpeg。虽惊讶于其宣称的速度,但老夫着实不信,待用代码验证了再写几篇文章。

本文固定链接: http://www.latelee.org/programming-under-linux/libjpeg-learning2.html

如无特别说明,迟思堂工作室文章均为原创,转载请注明: libjpeg学习2:内存篇 | 迟思堂工作室

目前暂无评论

发表评论

*

快捷键:Ctrl+Enter