Understanding MotionJpeg & h264 routines for Digic 4

Started by reddeercity, February 10, 2018, 08:08:51 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

reddeercity

I have being digging in to my decompiled Rom from my 5D2 and I very interested in Motion Jpeg as it's 4:2:2 but only 8bit (bad part) .
You my say "will you have raw video that's better then mjpeg"
I say "yes and no" why because there many advantages of mjpeg , bandwidth , more gradable then h264 , etc. ... and of course people could list many more advantages to Raw Video.
One big reason I can think of is "documentaries" where , sometimes you just need to let the camera roll I think most of us understand that  right !  ;D
Ok so now you know my reasons for this now lets push this to the limits  8)
I looking to implement this to UHD/4k , makes senses now ?   for 2 main thinks .
1st of course to encode the UHD/4k stream to mjpeg.avi (thought I have know idea how get the encoder to compile a file) This would good for all cam for big resolution and slow SD card bandwidth . 2nd for correct framing in hdmi in images larger then 1080p/I e.g. 2k or 3.5k etc. ... .
Now if proves to be impossible for mjpeg.avi then of course I will try h264 as the parameters  aloud for 4k video  .

Below is same stuff that related to what I'm talking about from 5D2 Rom
ff0380f8 JpCoreFirm : %#lx
ff03810c JpCoreExtStream : %#lx
ff038124 pTestCallBack %#lx
ff03813c H264Size 0x0A100000 + [%#lx]

So 0x0A10000 + [something] 1920x1080p ? I assume

ff04a894 mvpGetMovieVideoData : MovieType(%d)
ff04a8bc StartResizeMotionJpeg(%d,%#x,%d,%#x)
ff04a8e4 mvpRequestDecResize : MovieType(%d)

down sizing from Full Liveview ? 5.6k

ff04ab84 mvpGetMovieKeyFrameNum : MovieType(%d)
ff04abac MOVR_GetMovKeyFrameNum(%#x)
ff04abc8 ChangeVramCallBack
ff04abdc ChangeNextUpdateVram cancel[%d](%#x)
ff04ac04 ChangeNextUpdateVram[%d](%#x)

Start the stream ?

ff04aeac SetRectColorParameter : unknown DispType (%#x)
ff04aedc CreateVram[%d](%#x,%d,%d,%d)
ff04af00 ChangeNextVram[%d](%#x,%d,%d)
ff04af20 ChangeNextVram cancel[%d](%#x

I think this  applying Picture Style profile

ff04b16c DecodeComplete : DecodeMotionJpeg Error(%d)
ff04b198 DecodeComplete : DecodeMotionJpeg Error -> StopPreparation
ff04b1d8 mvpCancelDecResize : MovieType(%d)
ff04c7a0 MovieFrameRateCallback
ff04c7bc StartPreparation : mvpGetMovieVideoData(frame:%d)
ff04c7f0 StartPreparation : mvpGetMovieVideoData(%#x)
ff04c820 StartPreparation : Cancel(Decode)
ff04c848 StartPreparation : StartDecodeMotionJpeg(time out)
ff04c87c StartPreparation : Cancel(Resize)

self explanatory I think


ff04ec6c mvpResizeCompleteCallback
ff04ec88 mvpRegisterCompleteDecResize : MovieType(%d)
ff04ecb8 SetResizePassParam in(%d,%d,%d,%d,%d,%d,%d)
ff04ece4 SetResizePassParam out(%d,%d,%d,%d,%d,%d,%d)
ff04ed14 SetResizePassParam : (%d,%d,%d,%d,%d,%d,%d)

maybe has to with line skipping & pixel binding ? not sure that all , I notice "MovieType" so could this be either .AVI or .Mov ?
lots of question but no answers yet !
It's a start  :D

reddeercity

AVI Reader , still looking for the writer
ff15f4f8 AviReaderTask : TakeSemaphore(%#x)
ff15f528 InitializeAviReader : CreateBinarySemaphore
ff15f554 AviRead
ff15f560 MoviePlayer\AviReader.c
ff15f578 fInitialize == TRUE
ff15f58c CloseAviReadFile : TakeSemaphore(%#x)
ff15f9bc StartAviRead : TakeSemaphore(%#x)
ff15f9fc 01wb
ff15fce4 00dc
ff15fd04 GetAviVideoData : TakeSemaphore(%#x)
ff1600f8 RIFF
ff160108 OpenAviFile : TakeSemaphore(%#x)
ff16034c FileFormatVer == 2
ff160360 FileFormatVer == 1
ff160644 MoviePlayer\FrameRateGenerator.c
ff160668 FALSE

tin2tin


reddeercity


tosher

but you can teach 5D2 just write JPG full frame 25 fps + - without packaging in the video, just sequencing into the folder?

а можно 5D2 научить просто писать JPG полный кадр 25 фпс +- без упаковки в видео просто в секвенцию в папку?

ItsMeLenny

The mjpeg is what's used to push a live feed over USB isn't it?

kyrobb

The mjpeg buffer A1ex was able to record was quite low resolution was it not?

reddeercity

Sorry for the long post , just so much info  8)
Quote from: ItsMeLenny on February 12, 2018, 12:22:39 PM
The mjpeg is what's used to push a live feed over USB isn't it?
Yes , it all ready done --I have no interest in that .
Quote from: kyrobb on February 13, 2018, 04:00:57 AM
The mjpeg buffer A1ex was able to record was quite low resolution was it not?
Yes, very ,very low resolution 1:1 1024x680 & 3x crop 1120x752 . these are the Liveview buffers  .

What I'm after is the full liveview jpeg buffer
F3672>    CtrlSrv:ffa41a3c:18:01: GetJpegInfo w:5616 h:3744
So I can do this
F376E>    CtrlSrv:ffafb10c:18:03: [V] (PUB) MakeHDVramFromJpeg 959
F379A>    CtrlSrv:ffafb128:18:01: [V] MakeHDVramFromJpeg Rot:0 Mag:0 Aspect:3

but instead of "MakeHDVramFromJpeg .... " I hope to implement "MakeUHDVramFromJpeg .... " , thou I'm not sure at this point it all just theoretical .
This research has actually came from , me investigating my Startup LOG from "Jenkins" Thanks @a1ex  :D
I originally was looking for the compressed raw for ProcessTwoInTwoOutLosslessPath  84B5D> FrontShtDe:ff888ee0:96:05: ProcessTwoInTwoOutJpegPath(R) Start(5694)
AF6FE> FrontShtDe:ff888f10:96:05: ProcessTwoInTwoOutJpegPath(R) End(5694)

Of which I found & posted info here but that a whole different topic.

Here is some content for the startup log the related to JPCore
F37C5>    CtrlSrv:ffafb144:18:01: [V] Jpeg w:5616 h:3744
F37EE>    CtrlSrv:ffafb164:18:01: [V] WorkAdd:0x40D00000 VramAdd:0x5C007800 RotWork:0x0
F3826>    CtrlSrv:ffafb18c:18:01: [V] ImageWork w:2200 h1872 img_w:0 img_h:0 zoomx:0 zoomy:0

Full res to what will be the line skipping pixel binding image for Video.

F3889>    CtrlSrv:ffafabec:18:03: [V] MakeHDVram 758
F38B5>    CtrlSrv:ffafaa8c:18:03: [V] SetDecodeParam 712
F38E0>    CtrlSrv:ffaf849c:18:03: [L] (PUB) SetJpegDecodeParameter 189
F3909>    CtrlSrv:ffaf8284:18:03: [L] CalculateJpegDecodeParameter 85


F392E>    CtrlSrv:ffaf829c:18:01: [L] Jpeg w:5616 h:3744  Work w:2200 h:1872
The source to the working resized frame buffer

F3967>    CtrlSrv:ffaf81c4:18:03: [L] (PUB) SetDecodeRate 148
F398E>    CtrlSrv:ffaf99cc:18:03: [L] GetGCD 58


F39B9>    CtrlSrv:ffaf83a4:18:01: [L] Resize Pfil:1 Pres:1 w:3/8 h:3/8
F39F0>    CtrlSrv:ffaf8558:18:01: [L] Jpeg w:5616 h:3744
F3A18>    CtrlSrv:ffaf8578:18:01: [L] Yuv:1 Add:0x40D00000 BaseW:2106
F3A4B>    CtrlSrv:ffaf8598:18:01: [L] x:0 y:0 w:2106 h:1404
F3A7B>    CtrlSrv:ffafacbc:18:01: [V] In w:2106 h:1404 Out w:340 h:204

Some resizing going on

F3AB2>    CtrlSrv:ffaf975c:18:03: [L] (PUB) CalcResizeParam 961
F3AD7>    CtrlSrv:ffaf9778:18:01: [L] CalcResizeParam inw:2106 h:1404 outw:340 h:204
F3B04>    CtrlSrv:ffaf9790:18:01: [L] rot:0 zoom:0 inYUV:1 outYUV:1
F3B2F>    CtrlSrv:ffaf9368:18:03: [L] _CalcResizeSize 733

F3B5D>    CtrlSrv:ffaf8da0:18:03: [L] (PUB) CalcZoomMagnify 402
F3BA4>    CtrlSrv:ffaf8e20:18:03: [L] _RoundUp 498
F3BCC>    CtrlSrv:ffaf8da0:18:03: [L] (PUB) CalcZoomMagnify 402
F3BFB>    CtrlSrv:ffaf8e20:18:03: [L] _RoundUp 498

Preview window , I would think 5x & 10x

F3C29>    CtrlSrv:ffaf9668:18:01: [L] _CalcResizeSize InYuv:1 OutYuv:1 Aspect:3
F3C56>    CtrlSrv:ffaf968c:18:01: [L] fBaseH:0 MagWidth:2106 AspWidth:2106 MagHeight:1404 AspHeight:1404
F3C8C>    CtrlSrv:ffaf96a8:18:01: [L]  in w:2106 h:1404 out w:340 h:204
F3CBD>    CtrlSrv:ffaf9a30:18:03: [L] _CalcResizeRate 898
F3D19>    CtrlSrv:ffaf9838:18:01: [L] -----INPUT  w:2104 h:1404 -----
F3D4C>    CtrlSrv:ffaf9854:18:01: [L] -----OUTPUT w:340 h:204 -----
F3D78>    CtrlSrv:ffaf9864:18:01: [L] -----Resize-----

I still thinking preview window (3x_crop)

F3D9C>    CtrlSrv:ffaf9898:18:01: [L] Pfil:4 170/263 PreRes:4 68/117
F3DCD>    CtrlSrv:ffafad3c:18:01: [V] CalcResizeParam w:170/263(1/4) h:68/117(1/4)
F3E00>    CtrlSrv:ffafad5c:18:03: [V] SetResizeInputParam 487
F3E27>    CtrlSrv:ffafa4dc:18:03: [V] SetResizeOutputParam 524
F3E50>    CtrlSrv:ffafa568:18:03: [V] SetUnitYUV 119


I have the feeling that this is the evil "line skipping & pixel binding ?

F3E75>    CtrlSrv:ffafa5e8:18:01: [V] -----INPUT-----
F3E96>    CtrlSrv:ffafa604:18:01: [V] ZoomMode:0 YuvMode:1
F3EBB>    CtrlSrv:ffafa620:18:01: [V] BaseAdd:0x40D00000 BaseW:2106
F3EE9>    CtrlSrv:ffafa640:18:01: [V] x:0 y:0 w:2104 h:1404

1:1 Liveview before cropping ?

F3F11>    CtrlSrv:ffafa650:18:01: [V] -----OUTPUT-----
F3F31>    CtrlSrv:ffafa664:18:01: [V] YuvMode:1
F3F52>    CtrlSrv:ffafa680:18:01: [V] BaseAdd:0x5C007800 BaseW:720
F3F82>    CtrlSrv:ffafa6a0:18:01: [V] x:48 y:48 w:340 h:204

Lv plus preview window ? , 720 width =Lv  340 width=preview

F3FB0>    CtrlSrv:ffaf9f6c:18:03: [V] SetFirstColor 264
F3FD9>    CtrlSrv:ffafa01c:18:01: [V] SetFirstColor
F3FFE>    CtrlSrv:ffafa03c:18:01: [V] Yuv:1 Add:0x5C007800 BaseW:720
F402B>    CtrlSrv:ffafa058:18:01: [V] x:48 y:48 w:0 h:0
F4054>    CtrlSrv:ffafa1d0:18:03: [V] SetSecondColor 374
F4081>    CtrlSrv:ffafa2dc:18:01: [V] SetSecondColor
F40A5>    CtrlSrv:ffafa2fc:18:01: [V] Yuv:1 Add:0x5C007800 BaseW:720

Setting color profile for Picture Style , maybe .
There a lot of info to understand but I think the building blocks are there .
I still haven't found the Mjpeg encoder yet , but I have found where the h264 encoder is and there may be a very good chance to change the parameter to Full Res or 3840 .

H264 Rapper with it's encoding parameter , This is from my 5D2 decompiled ROM
ff18d208 [IMP][MDECROT] SetParameterMjpegDecRotate<---
ff18d4e8 [H264Rapper]Over 12/16 !!!! Ask Architect!!!!
ff18d518 [H264Rapper]
ff18d540 [H264Rapper]
ff18d568 [H264Rapper]
ff18d590 [H264Rapper] InputWidth=%d InputHeight=%d
ff18d5bc [H264Rapper] ResizedWidth=%d ResizedHeight=%d
ff18d5ec [H264Rapper] OupPutAspect %d : %d
ff18d610 [H264Rapper] ImageType==VGA
ff18d62c [H264Rapper] ImageType==FullHD
ff18d64c [H264Rapper] OFFSETY=%d
ff18d666 DUST=%d
ff18d670 [H264Rapper] f1PassMode==TRUE
ff18d690 [H264Rapper] f1PassMode==FALSE
ff18d6b0 [H264Rapper] THROUGH
ff18d6c8 [H264Rapper] QUARTER
ff18d6e0 [H264Rapper] HALF
ff18da48 [H264Rapper] DecodeHeight=%d Line=%d
ff18da74 [H264Rapper] DecodeWidth =%d DecodeHeight=%d Line=%d
ff18daac [H264Rapper] pDecodeWidth =%d DecodeHeight=%d Line=%d
ff18dae8 [H264Rapper] pOutputAddress=0x%08X
ff18db0c -------------------------------------%d
ff18de27 H264Rapper
ff18de33 ] OffX = %ld, OffY = %ld, W = %ld, H = %ld
ff18de60 [H264Rapper
ff18de6d ] OffX = %ld, OffY = %ld, W = %ld, H = %ld


These are from my decompiled rom 5d2 , as you see there's two streams for h264
HD & VGA ,  I hope we can get at least VGA h264  to work as a Proxy file .
Right now I have no idea just yet how I'm going to progress from here , I'll need help with coding all this of course .







reddeercity

found something interesting in 100d disassembly
"[IMP][MDECRSZ] Args of SetParameterMjpegDecResize":
ff40ebc4: e28f2f56 add r2, pc, #344 ; ff40ed24: (504d495b)  *"[IMP][MDECRSZ] Args of SetParameterMjpegDecResize"
ff40ebd8: e28f2f5e add r2, pc, #376 ; ff40ed58: (504d495b)  *"[IMP][MDECRSZ] pInputImage->BaseWidth  = %ld"
ff40ebec: e28f2f65 add r2, pc, #404 ; ff40ed88: (504d495b)  *"[IMP][MDECRSZ] pInputImage->Width      = %ld"
ff40ec00: e28f2e1b add r2, pc, #432 ; ff40edb8: (504d495b)  *"[IMP][MDECRSZ] pInputImage->Height     = %ld"
ff40ec14: e28f2f73 add r2, pc, #460 ; ff40ede8: (504d495b)  *"[IMP][MDECRSZ] pInputImage->OffsetX    = %ld"
ff40ec28: e28f2f7a add r2, pc, #488 ; ff40ee18: (504d495b)  *"[IMP][MDECRSZ] pInputImage->OffsetY    = %ld"
ff40ec3c: e28f2f81 add r2, pc, #516 ; ff40ee48: (504d495b)  *"[IMP][MDECRSZ] pOutputImage->BaseWidth = %ld"


ff40f6a0: e28f2f7e add r2, pc, #504 ; ff40f8a0: (504d495b)  *"[IMP][MDECROT] RequestMjpegDecRotate--->"
ff40f6b4: e28f2e21 add r2, pc, #528 ; ff40f8cc: (504d495b)  *"[IMP][MDECROT] pInputAddress1    = 0x%08lX"
ff40f6c8: e28f2f8a add r2, pc, #552 ; ff40f8f8: (504d495b)  *"[IMP][MDECROT] InputDataSize1    = %ld"
ff40f6dc: e28f2f8f add r2, pc, #572 ; ff40f920: (504d495b)  *"[IMP][MDECROT] pInputAddress2    = 0x%08lX"
ff40f6f0: e28f2f95 add r2, pc, #596 ; ff40f94c: (504d495b)  *"[IMP][MDECROT] InputDataSize2    = %ld"
ff40f704: e28f2f9a add r2, pc, #616 ; ff40f974: (504d495b)  *"[IMP][MDECROT] pOutputAddress    = 0x%08lX"

Of course 100d is digic5 , all we need is to found the right address to make it work on different models (I'm not saying it will work , just thinking out loud and hoping It will
inspire more people to get involved we development)  :D

reddeercity

5d2 digic4 and possible all digic4 models?

"[IMP][MDECRSZ] Args of SetParameterMjpegDecResize":

ff18bf44: e28f2f53 add r2, pc, #332 ; ff18c098: (504d495b)  *"[IMP][MDECRSZ] Args of SetParameterMjpegDecResize"
ff18bf54: e28f2e17 add r2, pc, #368 ; ff18c0cc: (504d495b)  *"[IMP][MDECRSZ] pInputImage->BaseWidth  = %ld"
ff18bf68: e28f2f63 add r2, pc, #396 ; ff18c0fc: (504d495b)  *"[IMP][MDECRSZ] pInputImage->Width      = %ld"
ff18bf7c: e28f2f6a add r2, pc, #424 ; ff18c12c: (504d495b)  *"[IMP][MDECRSZ] pInputImage->Height     = %ld"
ff18bf90: e28f2f71 add r2, pc, #452 ; ff18c15c: (504d495b)  *"[IMP][MDECRSZ] pInputImage->OffsetX    = %ld"
ff18bfa4: e28f2e1e add r2, pc, #480 ; ff18c18c: (504d495b)  *"[IMP][MDECRSZ] pInputImage->OffsetY    = %ld"
ff18bfb8: e28f2f7f add r2, pc, #508 ; ff18c1bc: (504d495b)  *"[IMP][MDECRSZ] pOutputImage->BaseWidth = %ld"
ff18bfcc: e28f2f86 add r2, pc, #536 ; ff18c1ec: (504d495b)  *"[IMP][MDECRSZ] pOutputImage->OffsetX   = %ld"
ff18bfe0: e28f2f8d add r2, pc, #564 ; ff18c21c: (504d495b)  *"[IMP][MDECRSZ] pOutputImage->OffsetY   = %ld"
ff18bff4: e28f2e25 add r2, pc, #592 ; ff18c24c: (504d495b)  *"[IMP][MDECRSZ] pOutputImage->Width     = %ld"
ff18c008: e28f2f9b add r2, pc, #620 ; ff18c27c: (504d495b)  *"[IMP][MDECRSZ] pOutputImage->Height    = %ld"
ff18c01c: e28f2fa2 add r2, pc, #648 ; ff18c2ac: (504d495b)  *"[IMP][MDECRSZ] RotMov                  = %ld"
ff18c030: e28f2fa9 add r2, pc, #676 ; ff18c2dc: (504d495b)  *"[IMP][MDECRSZ] Display                 = %ld"


ff18c390: e28f2f56 add r2, pc, #344 ; ff18c4f0: (504d495b)  *"[IMP][MDECRSZ] InputRate  X = %ld, Y = %ld"
ff18c3ac: e28f2f5a add r2, pc, #360 ; ff18c51c: (504d495b)  *"[IMP][MDECRSZ] OutputRate X = %ld, Y = %ld"
ff18c3c8: e28f2f5e add r2, pc, #376 ; ff18c548: (504d495b)  *"[IMP][MDECRSZ] pPbV Den = %ld, Num = %ld"
ff18c400: e28f2f5b add r2, pc, #364 ; ff18c574: (504d495b)  *"[IMP][MDECRSZ] pPbH Int = %ld, Dec = %ld"
ff18c40c: e28f2f63 add r2, pc, #396 ; ff18c5a0: (504d495b)  *"[IMP][MDECRSZ] OutputOffsetX         = %ld"
ff18c420: e28f2f69 add r2, pc, #420 ; ff18c5cc: (504d495b)  *"[IMP][MDECRSZ] OutputOffsetY         = %ld"
ff18c434: e28f2f6f add r2, pc, #444 ; ff18c5f8: (504d495b)  *"[IMP][MDECRSZ] ResizeWidth           = %ld"
ff18c448: e28f2f75 add r2, pc, #468 ; ff18c624: (504d495b)  *"[IMP][MDECRSZ] ResizeHeight          = %ld"


ff18c8fc: e28f2c02 add r2, pc, #512 ; ff18cb04: (504d495b)  *"[IMP][MDECROT] RequestMjpegDecRotate--->"
ff18c914: e28f2f85 add r2, pc, #532 ; ff18cb30: (504d495b)  *"[IMP][MDECROT] pInputAddress1    = 0x%08lX"
ff18c928: e28f2f8b add r2, pc, #556 ; ff18cb5c: (504d495b)  *"[IMP][MDECROT] InputDataSize1    = %ld"
ff18c93c: e28f2d09 add r2, pc, #576 ; ff18cb84: (504d495b)  *"[IMP][MDECROT] pInputAddress2    = 0x%08lX"
ff18c950: e28f2f96 add r2, pc, #600 ; ff18cbb0: (504d495b)  *"[IMP][MDECROT] InputDataSize2    = %ld"
ff18c964: e28f2f9b add r2, pc, #620 ; ff18cbd8: (504d495b)  *"[IMP][MDECROT] pOutputAddress    = 0x%08lX"
ff18c99c: 128f2f99 addne r2, pc, #612 ; ff18cc08: (504d495b)  *"[IMP][PBRSZ] ERROR LockEngineResources %d"


reddeercity

ff24b70c: e28f2e1e add r2, pc, #480 ; ff24b8f4: (34363248)  *"H264E InitializeH264EncodeFor720p
Strange ,I never thought the5d2 ever did 720p(maybe a after thought) , or is this a carry over from 7D or t2i ,interesting
ff24b844: e28f20f0 add r2, pc, #240 ; ff24b93c: (34363248)  *"H264E InitializeH264EncodeForCropVGA"
Proxy in 3xcrop mode? maybe , After reading the disassembly , I'm almost convinced the UHD h264 maybe possible , would need to by pass the
resizing routines from MJpeg stream .

reddeercity

Not to sure how to implement this ,
found open source mjpeg writer (encoder) written in "C" code and the creation .avi file code
on sourceforge.net , hoping it can be modified to work as a module  :))

mjpegwrt.c

*/
#include "mjpegwrt.h"

#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>

#include "types.h"

#ifdef __GLIBC__
#include <unistd.h>
#endif

#ifndef O_BINARY
#define O_BINARY 0
#endif

#if defined(WIN32) || defined (_WIN32)
#define fsync _commit
#endif

#define HEADERBYTES 0x1000 // 4k
#define DEFCACHE_SZ 0x100000 // 1M

#define MAXSOFTDESC_SZ 30
#define MAXCOMMENT_SZ 64
#define MAXDATE_SZ 22

#pragma pack(push, 2)

struct RiffChunk
{
__uint32_t ckID; // fourcc
__uint32_t ckSize; // size of chunk data
unsigned char *ckData; // chunk data
};

// size - 56
struct AVIHeader
{
__uint32_t dwMicroSecPerFrame;
__uint32_t dwMaxBytesPerSec; //
__uint32_t dwReserved1; // must be 0
__uint32_t dwFlags; // 0 ?
__uint32_t dwTotalFrames;
__uint32_t dwInitialFrames; // here must be 0
__uint32_t dwStreams; // number of streams
__uint32_t dwSuggestedBufferSize;
__uint32_t dwWidth; // width of frame
__uint32_t dwHeight; // height of frame
__uint32_t dwReserved[4]; // all must be 0
};

// size - 56
struct AVIStreamHeader
{
__uint32_t fccType; // 'vids'
__uint32_t fccHandler; // 'mjpg'
__uint32_t dwFlags; // here 0
__uint16_t wPriority; // here 0
__uint16_t wLanguage; // here 0
__uint32_t dwInitialFrames; // here 0
__uint32_t dwScale; // dwMicroSecPerFrame
__uint32_t dwRate; // 1000000
__uint32_t dwStart; // here 0
__uint32_t dwLength; // dwTotalFrames
__uint32_t dwSuggestedBufferSize; //  size largest chunk in the stream
__uint32_t dwQuality; // from 0 to 10000
__uint32_t dwSampleSize; // here 0
struct // here all field zero
{
__uint16_t left;
__uint16_t top;
__uint16_t right;
__uint16_t bottom;
} rcFrame;
};

// size - 40
struct AVIStreamFormat
{
__uint32_t biSize; // must be 40
__int32_t   biWidth; // width of frame
__int32_t   biHeight; // height of frame
__uint16_t biPlanes; // here must be 1
__uint16_t biBitCount; // here must be 24
__uint32_t biCompression; // here 'MJPG' or 0x47504A4D
__uint32_t biSizeImage; // size, in bytes, of the image (in D90_orig.avi 2764800)
__int32_t   biXPelsPerMeter; // here 0
__int32_t   biYPelsPerMeter; // here 0
__uint32_t biClrUsed; // here 0
__uint32_t biClrImportant; // here 0
};

struct AVIIndexChunk
{
__uint32_t ckID;
__uint32_t flags; // unknown?
__uint32_t offset; // offset from 'movi' to video frame chunk
__uint32_t size; // size of video frame chunk
};

#pragma pack(pop)

typedef struct
{
int fd; // file descriptor
__uint32_t realHeaderSize;
__uint32_t frames;
double fps;
unsigned char* index;
__uint32_t index_curpos;
__uint32_t* pindex_real_size;
__uint32_t index_size;
unsigned char* header;
__uint32_t *pFileSize;
__uint32_t *pDataSize;
struct AVIHeader* aviheader;
struct AVIStreamHeader* avistreamheader;
struct AVIStreamFormat* avistreamformat;
char* pSoftStr;
char* pCommentStr;
char* pDateStr;
unsigned char* cache;
unsigned int cache_sz;
unsigned int cache_pos;
} RIFFFILE;

void* mjpegCreateFile(const char* fname)
{
int fd = open(fname, O_BINARY | O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd < 0)
return 0;

RIFFFILE* riff = (RIFFFILE*)malloc(sizeof(RIFFFILE));
if (!riff)
{
close(riff->fd);
return 0;
}
memset(riff, 0, sizeof(RIFFFILE));
riff->fd = fd;
riff->header = (unsigned char*)malloc(HEADERBYTES);
if (!riff->header)
{
close(riff->fd);
free(riff);
return 0;
}
memset(riff->header, 0, HEADERBYTES);

riff->index_size = 0x100;
riff->index = (unsigned char*)malloc(riff->index_size);
if (!riff->index)
{
close(riff->fd);
free(riff->header);
free(riff);
return 0;
}

riff->pFileSize = (__uint32_t*)(riff->header + 4);
*riff->pFileSize = HEADERBYTES - 8;
__uint32_t* pnum = 0;
__uint32_t offset = 0;
char* ptr = (char*)riff->header; // addr = 0
strncpy(ptr, "RIFF", 4);
offset += 8;
ptr = (char*)(riff->header + offset); // addr = 8
strncpy(ptr, "AVI LIST", 8);
offset += 8;
pnum = (__uint32_t*)(riff->header + offset); // addr = 16
*pnum = 40 + sizeof(struct AVIHeader) + sizeof(struct AVIStreamHeader) + sizeof(struct AVIStreamFormat);
offset += 4;
ptr = (char*)(riff->header + offset); // addr = 20
strncpy(ptr, "hdrlavih", 8);
offset += 8;
pnum = (__uint32_t*)(riff->header + offset); // addr = 28
*pnum = sizeof(struct AVIHeader);
offset += 4;
riff->aviheader = (struct AVIHeader*)(riff->header + offset); // addr = 32
riff->aviheader->dwStreams = 1; // only video stream
riff->aviheader->dwFlags = 0x110; // has index & interlieved
offset += sizeof(struct AVIHeader);
ptr = (char*)(riff->header + offset); // addr = 88
strncpy(ptr, "LIST", 4);
offset += 4;
pnum = (__uint32_t*)(riff->header + offset); // addr = 92
*pnum = 20 + sizeof(struct AVIStreamHeader) + sizeof(struct AVIStreamFormat);
offset += 4;
ptr = (char*)(riff->header + offset); // addr = 96
strncpy(ptr, "strlstrh", 8);
offset += 8;
pnum = (__uint32_t*)(riff->header + offset); // addr = 104
*pnum = sizeof(struct AVIStreamHeader);
offset += 4;
riff->avistreamheader = (struct AVIStreamHeader*)(riff->header + offset); // addr = 108
riff->avistreamheader->fccType = 0x73646976; // 'vids'
riff->avistreamheader->fccHandler = 0x67706a6d; // 'mjpg'
offset += sizeof(struct AVIStreamHeader);
ptr = (char*)(riff->header + offset); // addr = 164
strncpy(ptr, "strf", 4);
offset += 4;
pnum = (__uint32_t*)(riff->header + offset); // addr = 168
*pnum = sizeof(struct AVIStreamFormat);
offset += 4;
riff->avistreamformat = (struct AVIStreamFormat*)(riff->header + offset); // addr = 172
offset += sizeof(struct AVIStreamFormat);

ptr = (char*)(riff->header + offset); // addr = 212
strncpy(ptr, "LIST", 4);
offset += 4;
pnum = (__uint32_t*)(riff->header + offset); // addr = 216
*pnum = 12 + MAXSOFTDESC_SZ + 8 + MAXCOMMENT_SZ + 8 + MAXDATE_SZ; // ISFT, ICMT & ICRD
offset += 4;
ptr = (char*)(riff->header + offset); // addr = 220
strncpy(ptr, "INFOISFT", 8);
offset += 8;
pnum = (__uint32_t*)(riff->header + offset); // addr = 228
*pnum = MAXSOFTDESC_SZ; // ISFT
offset += 4;
riff->pSoftStr = (char*)(riff->header + offset); // addr = 232
// fill this later
offset += MAXSOFTDESC_SZ;
ptr = (char*)(riff->header + offset); // addr = 262
strncpy(ptr, "ICMT", 4);
offset += 4;
pnum = (__uint32_t*)(riff->header + offset); // addr = 266
*pnum = MAXCOMMENT_SZ; // ICMT
offset += 4;
riff->pCommentStr = (char*)(riff->header + offset); // addr = 270
// fill this later
offset += MAXCOMMENT_SZ;

ptr = (char*)(riff->header + offset); // addr = 334
strncpy(ptr, "ICRD", 4);
offset += 4;
pnum = (__uint32_t*)(riff->header + offset); // addr = 338
*pnum = MAXDATE_SZ; // ICRD
offset += 4;
riff->pDateStr = (char*)(riff->header + offset); // addr = 342
// fill this later
offset += MAXDATE_SZ;

riff->realHeaderSize = offset;

// JUNK chunk
ptr = (char*)(riff->header + offset); // addr = 364
strncpy(ptr, "JUNK", 4);
offset += 4;
__uint32_t junk_size = HEADERBYTES - riff->realHeaderSize - 20;
pnum = (__uint32_t*)(riff->header + offset); // addr = 368
*pnum = junk_size;
offset += 4;

ptr = (char*)(riff->header + HEADERBYTES - 12); // addr = 4084
strncpy(ptr, "LIST", 4);
riff->pDataSize = (__uint32_t*)(riff->header + HEADERBYTES - 8); // 4088
*riff->pDataSize = 4;
ptr += 8; // addr = 4092
strncpy(ptr, "movi", 4);

// for index chunk
riff->pindex_real_size = (__uint32_t*)(riff->index + 4);
ptr = (char*)riff->index;
strncpy(ptr, "idx1", 4);
*riff->pindex_real_size = 0;
riff->index_curpos = 8;
*riff->pFileSize += 8;

riff->cache_sz = DEFCACHE_SZ;
riff->cache_pos = 0;
riff->cache = (unsigned char*)malloc(riff->cache_sz);
if (!riff->cache)
{
close(riff->fd);
free(riff->header);
free(riff->index);
free(riff);
return 0;
}

if (!write(riff->fd, riff->header, HEADERBYTES))
{
close(riff->fd);
free(riff->header);
free(riff->index);
free(riff);
return 0;
}

return (void*)riff;
}

static int cached_write(RIFFFILE* rf, const void* buf, unsigned int sz)
{
if (!rf || !rf->cache)
return -1;
int res = 0;
int inbuf_pos = 0;
char* ptr = (char*)rf->cache + rf->cache_pos;
char* buf_ptr = (char*)buf;
if (rf->cache_pos + sz > rf->cache_sz)
{
inbuf_pos = rf->cache_sz - rf->cache_pos;
memcpy(ptr, buf_ptr, inbuf_pos);
res = write(rf->fd, rf->cache, rf->cache_sz);
if (res > 0)
{
rf->cache_pos = 0;
ptr = (char*)rf->cache;
}
else
return res;

buf_ptr += inbuf_pos;
int full_count = (sz - inbuf_pos)/rf->cache_sz;
if (full_count > 0)
{
unsigned int f_pos = full_count*rf->cache_sz;
res = write(rf->fd, buf_ptr, f_pos);
if (res != f_pos)
return res;
inbuf_pos += f_pos;
buf_ptr += f_pos;
}
}
memcpy(ptr, buf_ptr, sz - inbuf_pos);
rf->cache_pos += sz - inbuf_pos;
return sz;
}

static int cache_flush(RIFFFILE* rf)
{
if (!rf || !rf->cache)
return -1;
if (rf->cache_pos > 0)
{
int res = write(rf->fd, rf->cache, rf->cache_pos);
if (res > 0)
rf->cache_pos = 0;
fsync(rf->fd);
return res;
}
return 0;
}

int mjpegSetup(void* p, int fwidth, int fheight, double fps, int quality)
{
RIFFFILE* rf = (RIFFFILE*)p;
if (!rf)
return 0;
rf->fps = fps;
//memset(rf->aviheader, 0, sizeof(struct AVIHeader));
rf->aviheader->dwMicroSecPerFrame = (__uint32_t)(1000000.0/fps);
rf->aviheader->dwWidth = fwidth;
rf->aviheader->dwHeight = fheight;
//memset(rf->avistreamheader, 0, sizeof(struct AVIStreamHeader));
rf->avistreamheader->dwScale = 1000;
rf->avistreamheader->dwRate = (__uint32_t)(1000.0*fps);
rf->avistreamheader->dwQuality = quality;
//memset(rf->avistreamformat, 0, sizeof(struct AVIStreamFormat));
rf->avistreamformat->biSize = 40;
rf->avistreamformat->biWidth = fwidth;
rf->avistreamformat->biHeight = fheight;
rf->avistreamformat->biPlanes = 1;
rf->avistreamformat->biBitCount = 24;
rf->avistreamformat->biCompression = 0x47504A4D; // 'MJPG'
rf->avistreamformat->biSizeImage = fwidth*fheight*3;
return 1;
}

int mjpegSetInfo(void* p, const char* software, const char* comment, const char* date)
{
RIFFFILE* rf = (RIFFFILE*)p;
if (!rf)
return 0;
if (software)
{
strncpy(rf->pSoftStr, software, MAXSOFTDESC_SZ - 1);
rf->pSoftStr[MAXSOFTDESC_SZ - 1] = 0;
}
else
rf->pSoftStr[0] = 0;
if (comment)
{
strncpy(rf->pCommentStr, comment, MAXCOMMENT_SZ - 1);
rf->pCommentStr[MAXCOMMENT_SZ - 1] = 0;
}
else
rf->pCommentStr[0] = 0;
if (date)
{
strncpy(rf->pDateStr, date, MAXDATE_SZ - 1);
rf->pDateStr[MAXDATE_SZ - 1] = 0;
}
else
rf->pDateStr[0] = 0;
return 1;
}

int mjpegSetCache(void* p, int sz)
{
RIFFFILE* rf = (RIFFFILE*)p;
if (!rf)
return 0;
unsigned char* tmp = (unsigned char*)malloc(sz);
if (!tmp)
return 0;
free(rf->cache);
rf->cache = tmp;
rf->cache_sz = sz;
return 1;
}

int mjpegSetMaxChunkSize(void* p, unsigned int sz)
{
RIFFFILE* rf = (RIFFFILE*)p;
if (!rf)
return 0;
rf->aviheader->dwSuggestedBufferSize = sz;
rf->aviheader->dwMaxBytesPerSec = (int)(sz*rf->fps) + 1;
rf->avistreamheader->dwSuggestedBufferSize = sz;
return 1;
}

int mjpegWriteChunk(void* p, const unsigned char* jpeg_data, unsigned int size)
{
RIFFFILE* rf = (RIFFFILE*)p;
if (!rf)
return 0;
if (!rf->index)
return 0;
if (rf->index_curpos + sizeof(struct AVIIndexChunk) > rf->index_size)
{
unsigned char* p = (unsigned char*)realloc(rf->index, rf->index_size + 0x100);
if (p)
{
rf->index = p;
rf->pindex_real_size = (__uint32_t*)(rf->index + 4);
rf->index_size += 0x100;
}
else
{
printf("FATAL: Can't realloc memory for index chunk!\n");
free(rf->index);
rf->index = 0;
return 0;
}
}

char buff[9];
strncpy(buff, "00dc", 4);
__uint32_t* pnum = (__uint32_t*)(buff + 4);
*pnum = (__uint32_t)size;
if (cached_write(rf, buff, 8) != 8)
return 0;
if (cached_write(rf, jpeg_data, size) != size)
return 0;
if (size % 2 != 0)
{
char junk = 0;
if (cached_write(rf, &junk, 1) != 1)
return 0;
}
// fill index
__uint32_t key_frame = (__uint32_t)(rf->fps);
struct AVIIndexChunk* ick = (struct AVIIndexChunk*)(rf->index + rf->index_curpos);
ick->ckID = 0x63643030; // '00dc'
ick->flags = (rf->frames % key_frame == 0) ? 0x10 : 0;
ick->offset = *rf->pDataSize;
ick->size = (__uint32_t)size;
rf->index_curpos += sizeof(struct AVIIndexChunk);
*rf->pindex_real_size += sizeof(struct AVIIndexChunk);
// increase counters
*rf->pDataSize += size + 8;
*rf->pFileSize += size + 8 + sizeof(struct AVIIndexChunk);
rf->frames++;
if (size % 2 != 0)
{
*rf->pDataSize += 1;
*rf->pFileSize += 1;
}
return 1;
}

int mjpegCloseFile(void* p)
{
RIFFFILE* rf = (RIFFFILE*)p;
if (!rf)
return 0;

int res = 1;
// write index
if (rf->index)
{
if (cached_write(rf, rf->index, *rf->pindex_real_size + 8) != *rf->pindex_real_size + 8)
res = 0;
}
else
res = 0;
cache_flush(rf);
// write modified header
rf->aviheader->dwTotalFrames = rf->frames;
rf->avistreamheader->dwLength = rf->frames;
lseek(rf->fd, 0, SEEK_SET);
if (write(rf->fd, rf->header, HEADERBYTES) != HEADERBYTES)
res = 0;

close(rf->fd);
free(rf->header);
if (rf->index)
free(rf->index);
if (rf->cache)
free(rf->cache);
free(rf);
return res;
}

mjpegwrt.h
*/

/* Work with one file ONLY in ONE thread!!!
*/

#ifndef _mjpegwrt_h
#define _mjpegwrt_h

#ifdef __cplusplus
extern "C" {
#endif

void* mjpegCreateFile(const char* fname);
int mjpegSetup(void* rf, int fwidth, int fheight, double fps, int quality);
int mjpegSetInfo(void* rf, const char* software, const char* comment, const char* date);
int mjpegSetCache(void* rf, int sz);
int mjpegSetMaxChunkSize(void* rf, unsigned int sz);
int mjpegWriteChunk(void* rf, const unsigned char* jpeg_data, unsigned int size);
int mjpegCloseFile(void* rf);

#ifdef __cplusplus
}
#endif

#endif // _mjpegwrt_h