summaryrefslogtreecommitdiff
path: root/src/im_format_avi.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/im_format_avi.cpp')
-rw-r--r--src/im_format_avi.cpp668
1 files changed, 668 insertions, 0 deletions
diff --git a/src/im_format_avi.cpp b/src/im_format_avi.cpp
new file mode 100644
index 0000000..3de98d4
--- /dev/null
+++ b/src/im_format_avi.cpp
@@ -0,0 +1,668 @@
+/** \file
+ * \brief AVI - Windows Audio-Video Interleaved RIFF
+ *
+ * See Copyright Notice in im_lib.h
+ * $Id: im_format_avi.cpp,v 1.1 2008/10/17 06:10:16 scuri Exp $
+ */
+
+#include "im_format.h"
+#include "im_format_avi.h"
+#include "im_util.h"
+#include "im_counter.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <windows.h>
+#include <vfw.h>
+
+#include "im_dib.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <memory.h>
+
+
+static const char* iAVICompTable[15] =
+{
+ "NONE",
+ "RLE", // Microsoft RLE
+ "CINEPACK", // Cinepak Codec by Radius
+ "MSVC", // Microsoft Video 1
+ "M261", // Microsoft H.261 Video Codec
+ "M263", // Microsoft H.263 Video Codec
+ "I420", // Intel 4:2:0 Video Codec (same as M263)
+ "IV32", // Intel Indeo Video Codec 3.2
+ "IV41", // Intel Indeo Video Codec 4.5
+ "IV50", // Intel Indeo Video 5.1
+ "IYUV", // Intel IYUV Codec
+ "MPG4", // Microsoft MPEG-4 Video Codec V1
+ "MP42", // Microsoft MPEG-4 Video Codec V2
+ "DIVX", // DivX 5.0.4 Codec (must be installed)
+ "CUSTOM" // (show compression dialog)
+};
+
+class imFormatAVI: public imFormat
+{
+ PAVIFILE file;
+ PAVISTREAM stream;
+
+ imDib* dib;
+ float fps;
+ unsigned int rmask, gmask, bmask,
+ roff, goff, boff; /* pixel bit mask control when reading 16 and 32 bpp images */
+
+ PGETFRAME frame; // used when reading
+ int current_frame;
+
+ COMPVARS compvars; // used when writing
+ int use_compressor;
+
+ void ReadPalette(unsigned char* bmp_colors);
+ void WritePalette(unsigned char* bmp_colors);
+ void FixRGB(int bpp);
+ void InitMasks(imDib* dib);
+
+public:
+ imFormatAVI()
+ :imFormat("AVI",
+ "Windows Audio-Video Interleaved RIFF",
+ "*.avi;",
+ iAVICompTable,
+ 15,
+ 1)
+ {}
+ ~imFormatAVI() {}
+
+ int Open(const char* file_name);
+ int New(const char* file_name);
+ void Close();
+ void* Handle(int index);
+ int ReadImageInfo(int index);
+ int ReadImageData(void* data);
+ int WriteImageInfo();
+ int WriteImageData(void* data);
+ int CanWrite(const char* compression, int color_mode, int data_type) const;
+};
+
+void imFormatRegisterAVI(void)
+{
+ imFormatRegister(new imFormatAVI());
+}
+
+int imFormatAVI::Open(const char* file_name)
+{
+ /* initializes avi file library */
+ AVIFileInit();
+
+ /* open existing file */
+ HRESULT hr = AVIFileOpen(&file, file_name, OF_READ, NULL);
+ if (hr != 0)
+ {
+ AVIFileExit();
+
+ if (hr == AVIERR_FILEOPEN)
+ return IM_ERR_OPEN;
+ else if (hr == AVIERR_BADFORMAT || hr == REGDB_E_CLASSNOTREG)
+ return IM_ERR_FORMAT;
+ else
+ return IM_ERR_ACCESS;
+ }
+
+ /* get the video stream */
+ hr = AVIFileGetStream(file, &stream, streamtypeVIDEO, 0);
+ if (hr != 0)
+ {
+ AVIFileRelease(this->file);
+ AVIFileExit();
+
+ if (hr == AVIERR_NODATA)
+ return IM_ERR_DATA;
+ else
+ return IM_ERR_ACCESS;
+ }
+
+ /* get stream info */
+ AVISTREAMINFO streaminfo;
+ AVIStreamInfo(stream, &streaminfo, sizeof(AVISTREAMINFO));
+
+ this->image_count = streaminfo.dwLength;
+ this->fps = (float)streaminfo.dwRate / (float)streaminfo.dwScale;
+
+ if (streaminfo.fccHandler == mmioFOURCC('D','I','B',' '))
+ strcpy(this->compression, "NONE");
+ else if (streaminfo.fccHandler == mmioFOURCC('M','R','L','E'))
+ strcpy(this->compression, "RLE");
+ else if (streaminfo.fccHandler == mmioFOURCC('c','v','i','d'))
+ strcpy(this->compression, "CINEPACK");
+ else
+ {
+ DWORD handler = streaminfo.fccHandler;
+ this->compression[0] = (char)handler;
+ this->compression[1] = (char)(handler >> 8);
+ this->compression[2] = (char)(handler >> 16);
+ this->compression[3] = (char)(handler >> 24);
+ this->compression[4] = 0;
+ }
+
+ this->frame = 0;
+ this->use_compressor = 0;
+ this->dib = 0;
+ this->current_frame = 0;
+
+ return IM_ERR_NONE;
+}
+
+int imFormatAVI::New(const char* file_name)
+{
+ /* initializes avi file library */
+ AVIFileInit();
+
+ /* creates a new file */
+ HRESULT hr = AVIFileOpen(&file, file_name, OF_WRITE | OF_CREATE, NULL);
+ if (hr != 0)
+ {
+ AVIFileExit();
+
+ if (hr == AVIERR_FILEOPEN)
+ return IM_ERR_OPEN;
+ else if (hr == AVIERR_BADFORMAT || hr == REGDB_E_CLASSNOTREG)
+ return IM_ERR_FORMAT;
+ else
+ return IM_ERR_ACCESS;
+ }
+
+ this->frame = 0;
+ this->stream = 0;
+ this->use_compressor = 0;
+ this->dib = 0;
+
+ return IM_ERR_NONE;
+}
+
+void imFormatAVI::Close()
+{
+ if (this->dib) imDibDestroy(this->dib);
+
+ if (this->use_compressor)
+ {
+ ICSeqCompressFrameEnd(&this->compvars);
+ ICCompressorFree(&this->compvars);
+ }
+
+ if (this->frame) AVIStreamGetFrameClose(this->frame);
+ if (this->stream) AVIStreamRelease(this->stream);
+
+ AVIFileRelease(this->file);
+ AVIFileExit();
+}
+
+void* imFormatAVI::Handle(int index)
+{
+ if (index == 1)
+ return (void*)this->file;
+ else if (index == 2)
+ return (void*)this->stream;
+ else
+ return NULL;
+}
+
+int imFormatAVI::ReadImageInfo(int index)
+{
+ this->current_frame = index;
+
+ if (this->frame) // frame reading already prepared
+ return IM_ERR_NONE;
+
+ /* get stream format */
+ LONG formsize;
+ AVIStreamReadFormat(stream, 0, NULL, &formsize);
+ BITMAPINFO *bmpinfo = (BITMAPINFO*)malloc(formsize);
+ HRESULT hr = AVIStreamReadFormat(stream, 0, bmpinfo, &formsize);
+ if (hr != 0)
+ {
+ free(bmpinfo);
+ return IM_ERR_ACCESS;
+ }
+
+ int top_down = 0;
+ if (bmpinfo->bmiHeader.biHeight < 0)
+ top_down = 1;
+
+ this->width = bmpinfo->bmiHeader.biWidth;
+ this->height = top_down? -bmpinfo->bmiHeader.biHeight: bmpinfo->bmiHeader.biHeight;
+
+ int bpp = bmpinfo->bmiHeader.biBitCount;
+
+ imAttribTable* attrib_table = AttribTable();
+ attrib_table->Set("FPS", IM_FLOAT, 1, &fps);
+
+ this->file_data_type = IM_BYTE;
+
+ if (bpp > 8)
+ {
+ this->file_color_mode = IM_RGB;
+ this->file_color_mode |= IM_PACKED;
+ }
+ else
+ {
+ this->palette_count = 1 << bpp;
+ this->file_color_mode = IM_MAP;
+ }
+
+ if (bpp < 8)
+ this->convert_bpp = bpp;
+
+ if (bpp == 32)
+ this->file_color_mode |= IM_ALPHA;
+
+ if (top_down)
+ this->file_color_mode |= IM_TOPDOWN;
+
+ if (bpp <= 8)
+ {
+ /* updates the palette_count based on the number of colors used */
+ if (bmpinfo->bmiHeader.biClrUsed != 0 &&
+ (int)bmpinfo->bmiHeader.biClrUsed < this->palette_count)
+ this->palette_count = bmpinfo->bmiHeader.biClrUsed;
+
+ ReadPalette((unsigned char*)bmpinfo->bmiColors);
+ }
+
+ free(bmpinfo);
+
+ this->line_buffer_extra = 4; // room enough for padding
+
+ /* prepares to read data from the stream */
+ frame = AVIStreamGetFrameOpen(stream, NULL);
+ if (!frame)
+ return IM_ERR_ACCESS;
+
+ return IM_ERR_NONE;
+}
+
+int imFormatAVI::WriteImageInfo()
+{
+ if (dib)
+ {
+ if (dib->bmih->biWidth != width || dib->bmih->biHeight != height ||
+ imColorModeSpace(file_color_mode) != imColorModeSpace(user_color_mode))
+ return IM_ERR_DATA;
+
+ return IM_ERR_NONE; // parameters can be set only once
+ }
+
+ // force bottom up orientation
+ this->file_data_type = IM_BYTE;
+ this->file_color_mode = imColorModeSpace(this->user_color_mode);
+
+ int bpp;
+ if (this->file_color_mode == IM_RGB)
+ {
+ this->file_color_mode |= IM_PACKED;
+ bpp = 24;
+
+ if (imColorModeHasAlpha(this->user_color_mode))
+ {
+ this->file_color_mode |= IM_ALPHA;
+ bpp = 32;
+
+ this->rmask = 0x00FF0000;
+ this->roff = 16;
+
+ this->gmask = 0x0000FF00;
+ this->goff = 8;
+
+ this->bmask = 0x000000FF;
+ this->boff = 0;
+ }
+ }
+ else
+ bpp = 8;
+
+ this->line_buffer_extra = 4; // room enough for padding
+
+ imAttribTable* attrib_table = AttribTable();
+
+ const void* attrib_data = attrib_table->Get("FPS");
+ if (attrib_data)
+ fps = *(float*)attrib_data;
+ else
+ fps = 15;
+
+ if (this->compression[0] == 0 || imStrEqual(this->compression, "NONE"))
+ this->use_compressor = 0;
+ else
+ this->use_compressor = 1;
+
+ dib = imDibCreate(width, height, bpp);
+
+ if (use_compressor)
+ {
+ memset(&compvars, 0, sizeof(COMPVARS));
+ compvars.cbSize = sizeof(COMPVARS);
+
+ if (imStrEqual(this->compression, "CUSTOM"))
+ {
+ if (ICCompressorChoose(NULL, ICMF_CHOOSE_DATARATE | ICMF_CHOOSE_KEYFRAME, dib->dib, NULL, &compvars, "Choose Compression") == FALSE)
+ return IM_ERR_COMPRESS;
+ }
+ else
+ {
+ compvars.dwFlags = ICMF_COMPVARS_VALID;
+ compvars.fccType = ICTYPE_VIDEO;
+
+ int* attrib = (int*)attrib_table->Get("KeyFrameRate");
+ if (attrib)
+ compvars.lKey = *attrib;
+ else
+ compvars.lKey = 15; // same defaults of the dialog
+
+ attrib = (int*)attrib_table->Get("DataRate");
+ if (attrib)
+ compvars.lDataRate = *attrib / 8;
+ else
+ compvars.lDataRate = 300; // same defaults of the dialog
+
+ attrib = (int*)attrib_table->Get("AVIQuality");
+ if (attrib)
+ compvars.lQ = *attrib;
+ else
+ compvars.lQ = (DWORD)ICQUALITY_DEFAULT;
+
+ if (imStrEqual(this->compression, "RLE"))
+ compvars.fccHandler = mmioFOURCC('M','R','L','E');
+ else if (imStrEqual(this->compression, "CINEPACK"))
+ compvars.fccHandler = mmioFOURCC('c','v','i','d');
+ else
+ compvars.fccHandler = mmioFOURCC(compression[0],compression[1],compression[2],compression[3]);
+
+ compvars.hic = ICOpen(ICTYPE_VIDEO, compvars.fccHandler, ICMODE_COMPRESS);
+ }
+
+ if (compvars.hic == NULL)
+ use_compressor = 0;
+ }
+
+ AVISTREAMINFO streaminfo;
+ memset(&streaminfo, 0, sizeof(AVISTREAMINFO));
+ streaminfo.fccType = streamtypeVIDEO;
+ streaminfo.dwScale = 1000;
+ streaminfo.dwRate = (DWORD)(fps*1000);
+ SetRect(&streaminfo.rcFrame, 0, 0, width, height);
+
+ if (use_compressor)
+ {
+ streaminfo.fccHandler = compvars.fccHandler;
+ streaminfo.dwQuality = compvars.lQ;
+ }
+ else
+ {
+ streaminfo.fccHandler = mmioFOURCC('D','I','B',' ');
+ streaminfo.dwQuality = (DWORD)ICQUALITY_DEFAULT;
+ }
+
+ /* creates a new stream in the new file */
+ HRESULT hr = AVIFileCreateStream(file, &stream, &streaminfo);
+ if (hr != 0)
+ return IM_ERR_ACCESS;
+
+ /* set stream format */
+ if (use_compressor)
+ {
+ if (!ICSeqCompressFrameStart(&compvars, dib->bmi))
+ return IM_ERR_COMPRESS;
+
+ hr = AVIStreamSetFormat(stream, 0, compvars.lpbiOut, dib->size - dib->bits_size);
+ }
+ else
+ hr = AVIStreamSetFormat(stream, 0, dib->dib, dib->size - dib->bits_size);
+
+ if (hr != 0)
+ return IM_ERR_ACCESS;
+
+ return IM_ERR_NONE;
+}
+
+void imFormatAVI::ReadPalette(unsigned char* bmp_colors)
+{
+ /* convert the color map to the IM format */
+ for (int c = 0; c < this->palette_count; c++)
+ {
+ int i = c * 4;
+ this->palette[c] = imColorEncode(bmp_colors[i + 2],
+ bmp_colors[i + 1],
+ bmp_colors[i]);
+ }
+}
+
+void imFormatAVI::WritePalette(unsigned char* bmp_colors)
+{
+ /* convert the color map to the IM format */
+ for (int c = 0; c < this->palette_count; c++)
+ {
+ int i = c * 4;
+ imColorDecode(&bmp_colors[i + 2], &bmp_colors[i + 1], &bmp_colors[i], this->palette[c]);
+ bmp_colors[i + 3] = 0;
+ }
+}
+
+void imFormatAVI::InitMasks(imDib* dib)
+{
+ if (dib->bmih->biCompression == BI_BITFIELDS)
+ {
+ unsigned int Mask;
+ unsigned int *PalMask = (unsigned int*)dib->bmic;
+
+ this->roff = 0;
+ this->rmask = Mask = PalMask[0];
+ while (!(Mask & 0x01) && (Mask != 0))
+ {Mask >>= 1; this->roff++;}
+
+ this->goff = 0;
+ this->gmask = Mask = PalMask[1];
+ while (!(Mask & 0x01) && (Mask != 0))
+ {Mask >>= 1; this->goff++;}
+
+ this->boff = 0;
+ this->bmask = Mask = PalMask[2];
+ while (!(Mask & 0x01) && (Mask != 0))
+ {Mask >>= 1; this->boff++;}
+ }
+ else
+ {
+ if (dib->bmih->biBitCount == 16)
+ {
+ this->rmask = 0x7C00;
+ this->roff = 10;
+
+ this->gmask = 0x03E0;
+ this->goff = 5;
+
+ this->bmask = 0x001F;
+ this->boff = 0;
+ }
+ else
+ {
+ this->rmask = 0x00FF0000;
+ this->roff = 16;
+
+ this->gmask = 0x0000FF00;
+ this->goff = 8;
+
+ this->bmask = 0x000000FF;
+ this->boff = 0;
+ }
+ }
+}
+
+void imFormatAVI::FixRGB(int bpp)
+{
+ int x;
+
+ switch (bpp)
+ {
+ case 16:
+ {
+ /* inverts the WORD values if not intel */
+ if (imBinCPUByteOrder() == IM_BIGENDIAN)
+ imBinSwapBytes2(this->line_buffer, this->width);
+
+ imushort* word_data = (imushort*)this->line_buffer;
+ imbyte* byte_data = (imbyte*)this->line_buffer;
+
+ // from end to start
+ for (x = this->width-1; x >= 0; x--)
+ {
+ imushort word_value = word_data[x];
+ int c = x*3;
+ byte_data[c] = (imbyte)((((rmask & word_value) >> roff) * 255) / (rmask >> roff));
+ byte_data[c+1] = (imbyte)((((gmask & word_value) >> goff) * 255) / (gmask >> goff));
+ byte_data[c+2] = (imbyte)((((bmask & word_value) >> boff) * 255) / (bmask >> boff));
+ }
+ }
+ break;
+ case 32:
+ {
+ unsigned int* dword_data = (unsigned int*)this->line_buffer;
+ imbyte* byte_data = (imbyte*)this->line_buffer;
+
+ for (x = 0; x < this->width; x++)
+ {
+ unsigned int dword_value = dword_data[x];
+ int c = x*3;
+ byte_data[c] = (imbyte)((rmask & dword_value) >> roff);
+ byte_data[c+1] = (imbyte)((gmask & dword_value) >> goff);
+ byte_data[c+2] = (imbyte)((bmask & dword_value) >> boff);
+ byte_data[c+3] = (imbyte)((0xFF000000 & dword_value) >> 24);
+ }
+ }
+ break;
+ default: // 24
+ {
+ imbyte* byte_data = (imbyte*)this->line_buffer;
+ for (x = 0; x < this->width; x++)
+ {
+ int c = x*3;
+ imbyte temp = byte_data[c]; // swap R and B
+ byte_data[c] = byte_data[c+2];
+ byte_data[c+2] = temp;
+ }
+ }
+ break;
+ }
+}
+
+int imFormatAVI::ReadImageData(void* data)
+{
+ imCounterTotal(this->counter, this->height, "Reading AVI Frame...");
+
+ void* packed_dib = AVIStreamGetFrame(this->frame, this->current_frame);
+ if (!packed_dib)
+ return IM_ERR_ACCESS;
+
+ dib = imDibCreateReference((imbyte*)packed_dib, NULL);
+
+ if (dib->bmih->biBitCount == 16 || dib->bmih->biBitCount == 32)
+ InitMasks(dib);
+ else if (dib->bmih->biBitCount <= 8)
+ {
+ this->palette_count = dib->palette_count;
+ ReadPalette((unsigned char*)dib->bmic);
+ }
+
+ imbyte* bits = dib->bits;
+ for (int row = 0; row < this->height; row++)
+ {
+ CopyMemory(this->line_buffer, bits, dib->line_size);
+ bits += dib->line_size;
+
+ if (dib->bmih->biBitCount > 8)
+ FixRGB(dib->bmih->biBitCount);
+
+ imFileLineBufferRead(this, data, row, 0);
+
+ if (!imCounterInc(this->counter))
+ {
+ imDibDestroy(dib);
+ dib = NULL;
+ return IM_ERR_COUNTER;
+ }
+ }
+
+ imDibDestroy(dib);
+ dib = NULL;
+ this->current_frame++;
+
+ return IM_ERR_NONE;
+}
+
+int imFormatAVI::WriteImageData(void* data)
+{
+ imCounterTotal(this->counter, this->height, "Writing AVI Frame...");
+
+ if (dib->bmih->biBitCount <= 8)
+ {
+ WritePalette((unsigned char*)dib->bmic);
+
+ /* this must be called here to update the palette */
+ AVIStreamSetFormat(this->stream, 0, dib->dib, dib->size - dib->bits_size);
+ }
+
+ imbyte* bits = dib->bits;
+ for (int row = 0; row < this->height; row++)
+ {
+ imFileLineBufferWrite(this, data, row, 0);
+
+ if (dib->bmih->biBitCount > 8)
+ FixRGB(dib->bmih->biBitCount);
+
+ CopyMemory(bits, this->line_buffer, dib->line_size);
+ bits += dib->line_size;
+
+ if (!imCounterInc(this->counter))
+ return IM_ERR_COUNTER;
+ }
+
+ bits = dib->bits;
+ LONG bits_size = dib->bits_size;
+ DWORD flags = 0;
+
+ if (this->use_compressor)
+ {
+ BOOL key = FALSE;
+ bits = (imbyte*)ICSeqCompressFrame(&this->compvars, 0, bits, &key, &bits_size);
+ if (key == TRUE)
+ flags = AVIIF_KEYFRAME;
+
+ if (!bits)
+ {
+ bits = dib->bits;
+ bits_size = dib->bits_size;
+ }
+ }
+
+ HRESULT hr = AVIStreamWrite(this->stream, this->image_count, 1, bits, bits_size, flags, NULL, NULL);
+ if (hr != 0)
+ return IM_ERR_ACCESS;
+
+ this->image_count++;
+
+ return IM_ERR_NONE;
+}
+
+int imFormatAVI::CanWrite(const char* compression, int color_mode, int data_type) const
+{
+ (void)compression;
+
+ int color_space = imColorModeSpace(color_mode);
+
+ if (color_space == IM_YCBCR || color_space == IM_LAB ||
+ color_space == IM_LUV || color_space == IM_XYZ ||
+ color_space == IM_CMYK)
+ return IM_ERR_DATA;
+
+ if (data_type != IM_BYTE)
+ return IM_ERR_DATA;
+
+ return IM_ERR_NONE;
+}