Reputation: 93
I'm working inside a homegrown C++ framework, where one of the classes interfaces indirectly to libjpeg-8c.so (obtained as a Ubuntu 16.04 synaptic package). I am running valgrind on my application, that eventually writes out image data as shown:
ImageDescriptor* pOutputImageDesc, int outputQuality)
{
//set up JPEG lib context
/* Step 4: Start compressor
* true ensures that we will write a complete interchange-JPEG file.
* Pass true unless you are very sure of what you're doing.
*/
jpeg_start_compress(&cInfo, TRUE);
/* Step 5: while (scan lines remain to be written)
* jpeg_write_scanlines(...);
* Here we use the library's state variable cInfo.next_scanline as the
* loop counter, so that we don't have to keep track ourselves.
* To keep things simple, we pass one scanline per call; you can pass
* more if you wish, though.
*/
rowStride = pInputImageDesc->width * pInputImageDesc->channels; // JSAMPLEs per row in image_buffer
while (cInfo.next_scanline < cInfo.image_height)
{
/* jpeg_write_scanlines expects an array of pointers to scanlines.
* Here the array is only one element long, but you could pass
* more than one scanline at a time if that's more convenient.
*/
rowPointerArray[0] = &inputBuffer[(cInfo.next_scanline * (rowStride))];
static_cast<void>(jpeg_write_scanlines(&cInfo, rowPointerArray, 1));
}
// Step 6: Finish compression
jpeg_finish_compress(&cInfo);
//setup external image context
// Step 7: release JPEG compression object
// This is an important step since it will release a good deal of memory.
jpeg_destroy_compress(&cInfo);
return 0;
}
Contrary to what the comment says, not all heap memory gets released. Valgrind thinks differently in this malloc() trace
NOTE: the trace below comes after linking against libjpeg-8d source
==6025== at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==6025== by 0x4E4CDCE: empty_mem_output_buffer (jdatadst.c:131)
==6025== by 0x4E4373E: dump_buffer_s (jchuff.c:274)
==6025== by 0x4E4373E: emit_bits_s (jchuff.c:329)
==6025== by 0x4E4373E: encode_one_block (jchuff.c:990)
==6025== by 0x4E4373E: encode_mcu_huff (jchuff.c:1040)
==6025== by 0x4E40DAB: compress_data (jccoefct.c:208)
==6025== by 0x4E45B3F: process_data_simple_main (jcmainct.c:135)
==6025== by 0x4E3EEE3: jpeg_write_scanlines (jcapistd.c:108)
==6025== by 0x40DC4E: CompressRawToJpg(ImageDescriptor*, ImageDescriptor*, int) (ImageCaptureHelper.cpp:646)
==6025== by 0x40DFB2: WriteJpgImage(char*, ImageDescriptor*) (ImageCaptureHelper.cpp:756)
==6025== by 0x40CB18: Recognition::OutputImage::encode() (output_image.cpp:58)
==6025== by 0x403AD4: main (testImgNorm.cpp:127)
So, after looking at the mem_empty_output_buffer() call, I'm thinking that this memory leak is due to:
1 - libjpeg-8c is badly written (it's certainly not clear how this fct and its caller interface either, but I digress); however, given how widely used this lib is, I doubt it.
2 - CompressRawToJpeg() should be calling free() on the malloc'ed buffer that's somewhere in *j_compress_ptr
3 - this is a real bug in libjpeg, and I should use another library.
I'm hoping someone out there that has encountered a similar issue, can offer a way for my code to write out jpeg images, without bleeding out 64K memory chunks (per file).
Thanks, Charles
Upvotes: 3
Views: 401
Reputation: 93410
The problem is most likely that jpeg_mem_dest()
allocates its own memory and ignores whatever you give it as the 2nd parameter:
uint8_t* output = NULL;
unsigned long outputSize = 0;
jpeg_mem_dest(&cInfo, &output , &outputSize);
So the leak happens when free()
is not called at the end of the encoding process:
jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);
free(output);
Upvotes: 2