MJPEG to JPEG C++/Delphi

Some years go i received task to receive/record/play from DCS IP camera, it support MPEG4 and MJPEG stream via RTSP/RTP, so problem was:

1. Implement RTSP (just part that will enough to receive stream)
2. Parse/Record received stream

To implement RTSP was enough 1 day of RFC and Sockets dump learning, to recording stream i wrote simple mpeg4/mjpeg parser that split stream to frames and than we can use methods from avifil32.dll

But when i done my employer said: “If we chose to receive mjpeg stream i wanna have possibility to save frame to jpeg file”. That was a problem. i spent much time for research :( but task was done :)

So what we need for that?

a) Write to file JPEG header
b) Write DHT color segment
c) Remove Avi header from MJPEG frame and write result
d) thats all :)

#include "mjpegtojpeg.hpp"

bool mjpeg2Jpeg(const char *fileName, const byte *buffer, const int size)
{
HANDLE file_ = CreateFile(fileName,
GENERIC_WRITE | GENERIC_READ,
0, NULL,
CREATE_ALWAYS,
0, NULL);

if (file_ == INVALID_HANDLE_VALUE)
{
return false;
}

byte jpgHdr[] =
{
0xff, 0xd8,                  // SOI
0xff, 0xe0,                  // APP0
0x00, 0x10,                  // APP0 Hdr size
0x4a, 0x46, 0x49, 0x46, 0x00, // ID string
0x01, 0x01,                  // Version
0x00,                      // Bits per type
0x00, 0x00,                  // X density
0x00, 0x00,                  // Y density
0x00,                      // X Thumbnail size
0x00                      // Y Thumbnail size
};

unsigned long wrote = 0;
//write header
if (!WriteFile(file_, jpgHdr, sizeof (jpgHdr), &wrote, NULL))
{
CloseHandle(file_);
return false;
}

byte MJPGDHTSeg[0x1A4] =
{
/* JPEG DHT Segment for YCrCb omitted from MJPG data */
0xFF, 0xC4, 0x01, 0xA2,
0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00,
0x00, 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61,
0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24,
0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34,
0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56,
0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9,
0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9,
0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
0xF8, 0xF9, 0xFA, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01,
0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62,
0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A,
0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56,
0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8,
0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8,
0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8,
0xF9, 0xFA
};
//Write DHT color segment
if (!WriteFile (file_, MJPGDHTSeg, sizeof (MJPGDHTSeg), &wrote, NULL))
{
CloseHandle(file_);
return false;
}

//removing avi header
int tmp = *(buffer + 4);
tmp <<= 8;
tmp += *(buffer + 5) + 4;

//write frame
if (!WriteFile (file_, buffer + tmp, size - tmp, &wrote, NULL))
{
CloseHandle(file_);
return false;
}

CloseHandle(file_);
return true;
}
unit mjpegtojpeg;

interface
uses windows;

const
MJPGDHTSeg : array[1..$1A4] of byte =
(
{ JPEG DHT Segment for YCrCb omitted from MJPG data }
$FF, $C4, $01, $A2,
$00, $00, $01, $05, $01, $01, $01, $01, $01, $01, $00, $00, $00, $00, $00, $00, $00, $00,
$01, $02, $03, $04, $05, $06, $07, $08, $09, $0A, $0B, $01, $00, $03, $01, $01, $01, $01,
$01, $01, $01, $01, $01, $00, $00, $00, $00, $00, $00, $01, $02, $03, $04, $05, $06, $07,
$08, $09, $0A, $0B, $10, $00, $02, $01, $03, $03, $02, $04, $03, $05, $05, $04, $04, $00,
$00, $01, $7D, $01, $02, $03, $00, $04, $11, $05, $12, $21, $31, $41, $06, $13, $51, $61,
$07, $22, $71, $14, $32, $81, $91, $A1, $08, $23, $42, $B1, $C1, $15, $52, $D1, $F0, $24,
$33, $62, $72, $82, $09, $0A, $16, $17, $18, $19, $1A, $25, $26, $27, $28, $29, $2A, $34,
$35, $36, $37, $38, $39, $3A, $43, $44, $45, $46, $47, $48, $49, $4A, $53, $54, $55, $56,
$57, $58, $59, $5A, $63, $64, $65, $66, $67, $68, $69, $6A, $73, $74, $75, $76, $77, $78,
$79, $7A, $83, $84, $85, $86, $87, $88, $89, $8A, $92, $93, $94, $95, $96, $97, $98, $99,
$9A, $A2, $A3, $A4, $A5, $A6, $A7, $A8, $A9, $AA, $B2, $B3, $B4, $B5, $B6, $B7, $B8, $B9,
$BA, $C2, $C3, $C4, $C5, $C6, $C7, $C8, $C9, $CA, $D2, $D3, $D4, $D5, $D6, $D7, $D8, $D9,
$DA, $E1, $E2, $E3, $E4, $E5, $E6, $E7, $E8, $E9, $EA, $F1, $F2, $F3, $F4, $F5, $F6, $F7,
$F8, $F9, $FA, $11, $00, $02, $01, $02, $04, $04, $03, $04, $07, $05, $04, $04, $00, $01,
$02, $77, $00, $01, $02, $03, $11, $04, $05, $21, $31, $06, $12, $41, $51, $07, $61, $71,
$13, $22, $32, $81, $08, $14, $42, $91, $A1, $B1, $C1, $09, $23, $33, $52, $F0, $15, $62,
$72, $D1, $0A, $16, $24, $34, $E1, $25, $F1, $17, $18, $19, $1A, $26, $27, $28, $29, $2A,
$35, $36, $37, $38, $39, $3A, $43, $44, $45, $46, $47, $48, $49, $4A, $53, $54, $55, $56,
$57, $58, $59, $5A, $63, $64, $65, $66, $67, $68, $69, $6A, $73, $74, $75, $76, $77, $78,
$79, $7A, $82, $83, $84, $85, $86, $87, $88, $89, $8A, $92, $93, $94, $95, $96, $97, $98,
$99, $9A, $A2, $A3, $A4, $A5, $A6, $A7, $A8, $A9, $AA, $B2, $B3, $B4, $B5, $B6, $B7, $B8,
$B9, $BA, $C2, $C3, $C4, $C5, $C6, $C7, $C8, $C9, $CA, $D2, $D3, $D4, $D5, $D6, $D7, $D8,
$D9, $DA, $E2, $E3, $E4, $E5, $E6, $E7, $E8, $E9, $EA, $F2, $F3, $F4, $F5, $F6, $F7, $F8,
$F9, $FA
);

jpgHdr : array[1..20] of byte =
(
$ff, $d8,                     // SOI
$ff, $e0,                     // APP0
$00, $10,                     // APP0 Hdr size
$4a, $46, $49, $46, $00, // ID string
$01, $01,                     // Version
$00,                           // Bits per type
$00, $00,                     // X density
$00, $00,                     // Y density
$00,                           // X Thumbnail size
$00                           // Y Thumbnail size
);

function mjpeg2Jpeg(const fileName: string; const buffer : Pointer; const size : integer) : boolean;

implementation

function mjpeg2Jpeg(const fileName: string; const buffer : Pointer; const size : integer) : boolean;
var
wrote : longword;
tmp : integer;
file_ : THandle;
begin
Result := False;
file_ := CreateFile(PChar(fileName),
GENERIC_WRITE or GENERIC_READ,
0, nil,
CREATE_ALWAYS,
0, 0);

if (file_ = INVALID_HANDLE_VALUE) then
begin
exit;
end;
//write header
if (not WriteFile(file_, jpgHdr[1], 20, wrote, nil)) then
begin
CloseHandle(file_);
exit;
end;

if (not WriteFile (file_, MJPGDHTSeg[1], $1A4, wrote, nil)) then
begin
CloseHandle(file_);
exit;
end;

//removing avi header
tmp := Integer(Pointer(Integer(buffer) + 4));
tmp := tmp shl 8;
tmp := tmp + Integer(Pointer(Integer(buffer) + 5)) + 4;

//write frame
if (not WriteFile (file_, Pointer(Integer(buffer) + tmp)^, size - tmp, wrote, nil)) then
begin
CloseHandle(file_);
exit;
end;

CloseHandle(file_);
Result := True;
end;

end.

you can download sources here

4 Responses to “MJPEG to JPEG C++/Delphi”

  1. 1
    sasha Says:

    Thank you very much! You are really helped me!

  2. 2
    Progger Says:

    Year, thanks for helping ;)

  3. 3
    sathish Says:

    can you post the same code in c# ?

  4. 4
    Jindal Says:

    Can someone tell, how this same thing can be done in linux system?

Leave a Reply