summaryrefslogtreecommitdiffstats
path: root/tkmpeg/ezmpeg.c
diff options
context:
space:
mode:
Diffstat (limited to 'tkmpeg/ezmpeg.c')
-rw-r--r--tkmpeg/ezmpeg.c1040
1 files changed, 1040 insertions, 0 deletions
diff --git a/tkmpeg/ezmpeg.c b/tkmpeg/ezmpeg.c
new file mode 100644
index 0000000..3da406d
--- /dev/null
+++ b/tkmpeg/ezmpeg.c
@@ -0,0 +1,1040 @@
+//This file is part of ezMPEG
+//Copyright (C)2002 Ingo Oppermann ( ingo_opp@users.sourceforge.net / http://sourceforge.net/projects/ezmpeg/ )
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU General Public License
+//as published by the Free Software Foundation; either
+//version 2 of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+
+// Version 0.1
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include "ezmpeg.h"
+
+#define c(i) (((i) == 0) ? 0.3535534 : 0.5) // 1 / (0.5 * sqrt(2))
+
+#define LUMINANCE 1
+#define CHROMINANCE 2
+
+int zigzag[64] = {0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63};
+
+// Quantisierungsmatrix
+float intraquant[64] = {8.0, 16.0, 19.0, 22.0, 26.0, 27.0, 29.0, 34.0, 16.0, 16.0, 22.0, 24.0, 27.0, 29.0, 34.0, 37.0, 19.0, 22.0, 26.0, 27.0, 29.0, 34.0, 34.0, 38.0, 22.0, 22.0, 26.0, 27.0, 29.0, 34.0, 37.0, 40.0, 22.0, 26.0, 27.0, 29.0, 32.0, 35.0, 40.0, 48.0, 26.0, 27.0, 29.0, 32.0, 35.0, 40.0, 48.0, 58.0, 26.0, 27.0, 29.0, 34.0, 38.0, 46.0, 56.0, 69.0, 27.0, 29.0, 35.0, 38.0, 46.0, 56.0, 69.0, 83.0};
+
+// Cosinustabelle
+float fcostable[32];
+
+// Macroblock Address Increment VLC (Tabelle B.1)
+int macroblock_address_increment_code[36] = {0x0, 0x1, 0x3, 0x2, 0x3, 0x2, 0x3, 0x2, 0x7, 0x6, 0xB, 0xA, 0x9, 0x8, 0x7, 0x6, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x23, 0x22, 0x21, 0x20, 0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18, 0xF, 0x8};
+int macroblock_address_increment_length[36] = {0, 1, 3, 3, 4, 4, 5, 5, 7, 7, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11};
+
+#define MACROBLOCK_STUFFING 34
+#define MACROBLOCK_ESCAPE 35
+
+// Index des Arrays ist size, von 0 bis 8 (Tabelle D.12)
+int diff_dc_size_codes_lum[9] = {0x4, 0x0, 0x1, 0x5, 0x6, 0xE, 0x1E, 0x3E, 0x7E};
+int diff_dc_size_length_lum[9] = {3, 2, 2, 3, 3, 4, 5, 6, 7};
+int diff_dc_size_codes_chrom[9] = {0x0, 0x1, 0x2, 0x6, 0xE, 0x1E, 0x3E, 0x7E, 0xFE};
+int diff_dc_size_length_chrom[9] = {2, 2, 2, 3, 4, 5, 6, 7, 8};
+
+// Index des Arrays ist size, von 0 bis 8 (Tabelle D.13)
+int diff_dc_add_codes_pos[9] = {0x0, 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80};
+int diff_dc_add_codes_neg[9] = {0x0, 0x0, 0x1, 0x3, 0x7, 0xF, 0x1F, 0x3F, 0x7F};
+int diff_dc_add_length[9] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
+
+// Tabelle D.15, der Index ist level ohne Vorzeichen, das muss noch extra hinzugefügt werden!
+// Bei Index==0 steht drin, wieviele Levels da sind
+
+int ac_codes_intra0[41] = {40, 3, 4, 5, 6, 38, 33, 10, 29, 24, 19, 16, 26, 25, 24, 23, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 24, 23, 22, 21, 20, 19, 18, 17, 16};
+int ac_length_intra0[41] = {0, 2, 4, 5, 7, 8, 8, 10, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15};
+
+int ac_codes_intra1[19] = {18, 3, 6, 37, 12, 27, 22, 21, 31, 30, 29, 28, 27, 26, 25, 19, 18, 17, 16};
+int ac_length_intra1[19] = {0, 3, 6, 8, 10, 12, 13, 13, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16};
+
+int ac_codes_intra2[6] = {5, 5, 4, 11, 20, 20};
+int ac_length_intra2[6] = {0, 4, 7, 10, 12, 13};
+
+int ac_codes_intra3[5] = {4, 7, 36, 28, 19};
+int ac_length_intra3[5] = {0, 5, 8, 12, 13};
+
+int ac_codes_intra4[4] = {3, 6, 15, 18};
+int ac_length_intra4[4] = {0, 5, 10, 12};
+
+int ac_codes_intra5[4] = {3, 7, 9, 18};
+int ac_length_intra5[4] = {0, 6, 10, 13};
+
+int ac_codes_intra6[4] = {3, 5, 30, 20};
+int ac_length_intra6[4] = {0, 6, 12, 16};
+
+int ac_codes_intra7[3] = {2, 4, 21};
+int ac_length_intra7[3] = {0, 6, 12};
+
+int ac_codes_intra8[3] = {2, 7, 17};
+int ac_length_intra8[3] = {0, 7, 12};
+
+int ac_codes_intra9[3] = {2, 5, 17};
+int ac_length_intra9[3] = {0, 7, 13};
+
+int ac_codes_intra10[3] = {2, 39, 16};
+int ac_length_intra10[3] = {0, 8, 13};
+
+int ac_codes_intra11[3] = {2, 35, 26};
+int ac_length_intra11[3] = {0, 8, 16};
+
+int ac_codes_intra12[3] = {2, 34, 25};
+int ac_length_intra12[3] = {0, 8, 16};
+
+int ac_codes_intra13[3] = {2, 32, 24};
+int ac_length_intra13[3] = {0, 8, 16};
+
+int ac_codes_intra14[3] = {2, 14, 23};
+int ac_length_intra14[3] = {0, 10, 16};
+
+int ac_codes_intra15[3] = {2, 13, 22};
+int ac_length_intra15[3] = {0, 10, 16};
+
+int ac_codes_intra16[3] = {2, 8, 21};
+int ac_length_intra16[3] = {0, 10, 16};
+
+int ac_codes_intra17[2] = {1, 31};
+int ac_length_intra17[2] = {0, 12};
+
+int ac_codes_intra18[2] = {1, 26};
+int ac_length_intra18[2] = {0, 12};
+
+int ac_codes_intra19[2] = {1, 25};
+int ac_length_intra19[2] = {0, 12};
+
+int ac_codes_intra20[2] = {1, 23};
+int ac_length_intra20[2] = {0, 12};
+
+int ac_codes_intra21[2] = {1, 22};
+int ac_length_intra21[2] = {0, 12};
+
+int ac_codes_intra22[2] = {1, 31};
+int ac_length_intra22[2] = {0, 13};
+
+int ac_codes_intra23[2] = {1, 30};
+int ac_length_intra23[2] = {0, 13};
+
+int ac_codes_intra24[2] = {1, 29};
+int ac_length_intra24[2] = {0, 13};
+
+int ac_codes_intra25[2] = {1, 28};
+int ac_length_intra25[2] = {0, 13};
+
+int ac_codes_intra26[2] = {1, 27};
+int ac_length_intra26[2] = {0, 13};
+
+int ac_codes_intra27[2] = {1, 31};
+int ac_length_intra27[2] = {0, 16};
+
+int ac_codes_intra28[2] = {1, 30};
+int ac_length_intra28[2] = {0, 16};
+
+int ac_codes_intra29[2] = {1, 29};
+int ac_length_intra29[2] = {0, 16};
+
+int ac_codes_intra30[2] = {1, 28};
+int ac_length_intra30[2] = {0, 16};
+
+int ac_codes_intra31[2] = {1, 27};
+int ac_length_intra31[2] = {0, 16};
+
+int *ac_codes_intra[] = {
+ac_codes_intra0, ac_codes_intra1, ac_codes_intra2, ac_codes_intra3,
+ac_codes_intra4, ac_codes_intra5, ac_codes_intra6, ac_codes_intra7,
+ac_codes_intra8, ac_codes_intra9, ac_codes_intra10, ac_codes_intra11,
+ac_codes_intra12, ac_codes_intra13, ac_codes_intra14, ac_codes_intra15,
+ac_codes_intra16, ac_codes_intra17, ac_codes_intra18, ac_codes_intra19,
+ac_codes_intra20, ac_codes_intra21, ac_codes_intra22, ac_codes_intra23,
+ac_codes_intra24, ac_codes_intra25, ac_codes_intra26, ac_codes_intra27,
+ac_codes_intra28, ac_codes_intra29, ac_codes_intra30, ac_codes_intra31};
+
+int *ac_length_intra[] = {
+ac_length_intra0, ac_length_intra1, ac_length_intra2, ac_length_intra3,
+ac_length_intra4, ac_length_intra5, ac_length_intra6, ac_length_intra7,
+ac_length_intra8, ac_length_intra9, ac_length_intra10, ac_length_intra11,
+ac_length_intra12, ac_length_intra13, ac_length_intra14, ac_length_intra15,
+ac_length_intra16, ac_length_intra17, ac_length_intra18, ac_length_intra19,
+ac_length_intra20, ac_length_intra21, ac_length_intra22, ac_length_intra23,
+ac_length_intra24, ac_length_intra25, ac_length_intra26, ac_length_intra27,
+ac_length_intra28, ac_length_intra29, ac_length_intra30, ac_length_intra31};
+
+// Spezielle Codes
+int ac_codes_special[2] = {2, 1};
+int ac_length_special[2] = {2, 6};
+
+#define BLOCK_EOB 0
+#define BLOCK_ESCAPE 1
+
+int ezMPEG_Init(ezMPEGStream *ms, const char *outfile, int hsize, int vsize, int picture_rate, int gop_size, int q_scale)
+{
+ int i, j;
+
+ if(ms == NULL)
+ return 0;
+
+ if(outfile == NULL) {
+ ezMPEG_SetError(ms, "ezMPEG_Init: Please specify an output filename");
+ return 0;
+ }
+
+ if(hsize < 16 || hsize > 768) {
+ ezMPEG_SetError(ms, "ezMPEG_Init: Horizontal size should be between 16 and 768");
+ return 0;
+ }
+
+ if(vsize < 16 || vsize > 576) {
+ ezMPEG_SetError(ms, "ezMPEG_Init: Vertical size should be between 16 and 576");
+ return 0;
+ }
+
+ if(picture_rate != 25) {
+ ezMPEG_SetError(ms, "ezMPEG_Init: Picture rate should be 25 (for now)");
+ return 0;
+ }
+
+ if(gop_size == 0) {
+ ezMPEG_SetError(ms, "ezMPEG_Init: GOP size should be 1 or greater");
+ return 0;
+ }
+
+ if(q_scale < 1 || q_scale > 31) {
+ ezMPEG_SetError(ms, "ezMPEG_Init: Quantizer scale should be between 1 and 31");
+ return 0;
+ }
+
+ ms->outfile = (char *)malloc((strlen(outfile) + 1) * sizeof(char));
+ if(!ms->outfile) {
+ ezMPEG_SetError(ms, "ezMPEG_Init: Couldn't allocate enough memory");
+ return 0;
+ }
+ strcpy(ms->outfile, outfile);
+ ms->hsize = hsize / 16;
+ ms->vsize = vsize / 16;
+ ms->picture_rate = picture_rate; // picture_rate;
+ ms->gop_size = gop_size;
+ ms->dc_prev[0] = 128;
+ ms->dc_prev[1] = 128;
+ ms->dc_prev[2] = 128;
+ ms->q_scale = (float)q_scale;
+
+ // Versteckte Felder
+ ms->picture_count = 0;
+ ms->buffersize = 8192;
+ ms->buffercount = 0;
+ ms->buffer = (char *)malloc(ms->buffersize * sizeof(char));
+ if(!ms->buffer) {
+ ezMPEG_SetError(ms, "ezMPEG_Init: Couldn't allocate enough memory");
+ return 0;
+ }
+
+ ms->error_code = 0;
+ strcpy(ms->error_msg, "");
+
+ // Cosinustabelle füllen
+ for(i = 0; i < 8; i++) {
+ for(j = 0; j < 4; j++) {
+ if((i % 2)) // ungerade
+ fcostable[i * 4 + j] = cos((M_PI * (2.0 * (float)j + 1.0) / 16.0) + (M_PI * (float)((i - 1) / 2) * (2.0 * (float)j + 1.0) / 8.0));
+ else
+ fcostable[i * 4 + j] = cos(M_PI * (float)(i / 2) * (2.0 * (float)j + 1.0) / 8.0);
+ }
+ }
+
+ return 1;
+}
+
+int ezMPEG_Start(ezMPEGStream *ms)
+{
+ if(ms == NULL)
+ return 0;
+
+ // Datei zum Reinschreiben aufmachen
+ ms->out = fopen(ms->outfile, "wb");
+ if(ms->out == NULL) {
+ ezMPEG_SetError(ms, "ezMPEG_Start: Couldn't open output file");
+ return 0;
+ }
+
+ // BitBuffer initialisieren
+ ezMPEG_InitBitBuffer(ms);
+
+ // MPEG Video Sequence Header schreiben
+ ezMPEG_WriteSequenceHeader(ms);
+
+ if(ms->error_code)
+ return 0;
+
+ return 1;
+}
+
+void ezMPEG_WriteSequenceHeader(ezMPEGStream *ms)
+{
+ ezMPEG_ByteAlign(ms);
+
+ ezMPEG_WriteBits(ms, 0x1B3, 32); // sequence_header_code
+ ezMPEG_WriteBits(ms, ms->hsize & 0xFF, 8); // horizontal_size (in Macroblocks)
+ ezMPEG_WriteBits(ms, (ms->hsize >> 8) & 0xF, 4); //
+ ezMPEG_WriteBits(ms, ms->vsize & 0xFF, 8); // vertical_size (in Macroblocks)
+ ezMPEG_WriteBits(ms, (ms->vsize >> 8) & 0xF, 4); //
+ ezMPEG_WriteBits(ms, 1, 4); // pel_aspect_ratio (1:1)
+ ezMPEG_WriteBits(ms, 3, 4); // picture_rate (25Hz)
+
+ ezMPEG_WriteBits(ms, 0xFFFF, 16); // variable_length_bitrate (18x 1)
+ ezMPEG_WriteBits(ms, 0x3, 2); //
+
+ ezMPEG_WriteBits(ms, 1, 1); // marker_bit (1)
+ ezMPEG_WriteBits(ms, 20, 10); // vbv_buffer_size (20)
+ ezMPEG_WriteBits(ms, 1, 1); // constrained_parameter_flags (1)
+ ezMPEG_WriteBits(ms, 0, 1); // load_intra_quantizer_matrix (0)
+ ezMPEG_WriteBits(ms, 0, 1); // load_non_intra_quantizer_maxtrix (0)
+
+ if(ms->error_code)
+ strcpy(ms->error_msg, "ezMPEG_WriteSequenceHeader: Couldn't write sequence header");
+
+ return;
+}
+
+void ezMPEG_InitBitBuffer(ezMPEGStream *ms)
+{
+ ezMPEG_WriteBits(ms, 0, -1);
+
+ return;
+}
+
+void ezMPEG_ByteAlign(ezMPEGStream *ms)
+{
+ ezMPEG_WriteBits(ms, 0, -2);
+
+ return;
+}
+
+int ezMPEG_WriteBits(ezMPEGStream *ms, unsigned int value, int length)
+{
+ int temp;
+
+ if(length == -1) {
+ ms->buf = 0;
+ ms->pos = 0;
+ return 1;
+ }
+ if(length == -2) {
+ if(ms->pos == 0)
+ return 1;
+ length = 8 - ms->pos;
+ value = 0;
+ }
+
+ do {
+ if(length >= 8) {
+ temp = value >> ((length - 8) + ms->pos);
+ ms->buf += temp;
+ ms->buffer[ms->buffercount++] = ms->buf;
+ length -= (8 - ms->pos);
+ ms->pos = 0;
+ ms->buf = 0;
+ }
+ else {
+ if(length + ms->pos < 8) {
+ temp = value << (8 - length);
+ temp = temp & 0xFF;
+ temp = temp >> ms->pos;
+ ms->buf += temp;
+ ms->pos += length;
+ length = 0;
+ }
+ else {
+ temp = value << (8 - length);
+ temp = temp & 0xFF;
+ temp = temp >> ms->pos;
+ ms->buf += temp;
+ ms->buffer[ms->buffercount++] = ms->buf;
+ length -= (8 - ms->pos);
+ ms->buf = 0;
+ ms->pos = 0;
+ }
+ }
+
+ if(ms->buffercount >= ms->buffersize) {
+ if(fwrite(ms->buffer, ms->buffercount, 1, ms->out) < 1) {
+ ezMPEG_SetError(ms, "ezMPEG_WriteBits: Couldn't write buffer to file");
+ return 0;
+ }
+ ms->buffercount = 0;
+ }
+ }while(length != 0);
+
+ return 1;
+}
+
+int ezMPEG_Add(ezMPEGStream *ms, unsigned char *picture)
+{
+ int m;
+ Macroblock mb;
+
+ // Schauen, ob eine neue GOP angefangen werden muss
+ if(ms->picture_count % ms->gop_size == 0)
+ ezMPEG_WriteGOPHeader(ms);
+
+ ezMPEG_WritePictureHeader(ms);
+
+ for(m = 0; m < ms->hsize * ms->vsize; m++) {
+ // Das Bild in Macroblocks und YCbCr umwandeln
+ mb = ezMPEG_GetMacroblock(ms, picture, m /* Nummer */);
+
+ // Das Bild transformieren und scalieren
+ mb = ezMPEG_QuantizeMacroblock(ms, mb);
+
+ // Macroblock codieren und schreiben
+ ezMPEG_WriteMacroblock(ms, mb);
+ }
+
+ ms->picture_count++;
+
+ if(ms->error_code)
+ return 0;
+
+ return 1;
+}
+
+void ezMPEG_WriteMacroblock(ezMPEGStream *ms, Macroblock mb)
+{
+ int j, k, run, level, dc_diff;
+
+ // Macroblock Header, macroblock_address_increment
+ ezMPEG_WriteBits(ms, macroblock_address_increment_code[1], macroblock_address_increment_length[1]);
+
+ if(ms->q_scale != 1.0) {
+ ezMPEG_WriteBits(ms, 1, 2); // macroblock_type (I-Block, intra-q)
+ ezMPEG_WriteBits(ms, (int)ms->q_scale, 5); // nochmals quantizer_scale wie im Slice
+ }
+ else
+ ezMPEG_WriteBits(ms, 1, 1); // macroblock_type (I-Block, intra-d)
+
+ // Ueber jeden Block in diesem Macroblock gehen (zuerst Luminance)
+ for(j = 0; j < 4; j++) {
+
+ // DC-Koeffizienten codieren
+ mb.lum[j].a[0] += (mb.lum[j].a[0] > 0.0) ? 0.5 : -0.5;
+ dc_diff = (int)mb.lum[j].a[0] - ms->dc_prev[0];
+
+ ezMPEG_EncodeDC(ms, dc_diff, LUMINANCE);
+ ms->dc_prev[0] += dc_diff;
+
+ // AC-Koeffizienten Runlength codieren
+ run = 0;
+ level = 0;
+
+ for(k = 1; k < 64; k++) {
+ mb.lum[j].a[zigzag[k]] += (mb.lum[j].a[zigzag[k]] > 0.0) ? 0.5 : -0.5;
+ level = (int)mb.lum[j].a[zigzag[k]];
+ if(level != 0) {
+ ezMPEG_EncodeAC(ms, run, level);
+ run = 0;
+ }
+ else
+ run++;
+ }
+ ezMPEG_WriteBits(ms, ac_codes_special[BLOCK_EOB], ac_length_special[BLOCK_EOB]);
+ }
+
+ // Ueber den Cb-Block gehen
+ mb.chrom[0].a[0] += (mb.chrom[0].a[0] > 0.0) ? 0.5 : -0.5;
+ dc_diff = (int)mb.chrom[0].a[0] - ms->dc_prev[1];
+
+ ezMPEG_EncodeDC(ms, dc_diff, CHROMINANCE);
+ ms->dc_prev[1] += dc_diff;
+
+ run = 0;
+ level = 0;
+ for(k = 1; k < 64; k++) {
+ mb.chrom[0].a[zigzag[k]] += (mb.chrom[0].a[zigzag[k]] > 0.0) ? 0.5 : -0.5;
+ level = (int)mb.chrom[0].a[zigzag[k]];
+ if(level != 0) {
+ ezMPEG_EncodeAC(ms, run, level);
+ run = 0;
+ }
+ else
+ run++;
+ }
+ ezMPEG_WriteBits(ms, ac_codes_special[BLOCK_EOB], ac_length_special[BLOCK_EOB]);
+
+ // Ueber den Cr-Block gehen
+ mb.chrom[1].a[0] += (mb.chrom[1].a[0] > 0.0) ? 0.5 : -0.5;
+ dc_diff = (int)mb.chrom[1].a[0] - ms->dc_prev[2];
+
+ ezMPEG_EncodeDC(ms, dc_diff, CHROMINANCE);
+ ms->dc_prev[2] += dc_diff;
+
+ run = 0;
+ level = 0;
+ for(k = 1; k < 64; k++) {
+ mb.chrom[1].a[zigzag[k]] += (mb.chrom[1].a[zigzag[k]] > 0.0) ? 0.5 : -0.5;
+ level = (int)mb.chrom[1].a[zigzag[k]];
+ if(level != 0) {
+ ezMPEG_EncodeAC(ms, run, level);
+ run = 0;
+ }
+ else
+ run++;
+ }
+ ezMPEG_WriteBits(ms, ac_codes_special[BLOCK_EOB], ac_length_special[BLOCK_EOB]);
+
+ return;
+}
+
+void ezMPEG_WriteGOPHeader(ezMPEGStream *ms)
+{
+ ezMPEG_ByteAlign(ms);
+
+ ezMPEG_WriteBits(ms, 0x1B8, 32); // group_start_code
+ ezMPEG_WriteBits(ms, 0, 1); // time_code
+ ezMPEG_WriteBits(ms, ((ms->picture_count / ms->picture_rate) / 60) / 24, 5); // Stunden
+ ezMPEG_WriteBits(ms, ((ms->picture_count / ms->picture_rate) / 60) % 24, 6); // Minuten
+ ezMPEG_WriteBits(ms, 1, 1); //
+ ezMPEG_WriteBits(ms, (ms->picture_count / ms->picture_rate) % 60, 6); // Sekunden
+ ezMPEG_WriteBits(ms, ms->picture_count % ms->picture_rate, 6); // picture_time_code
+ ezMPEG_WriteBits(ms, 1, 1); // closed_gop (1)
+ ezMPEG_WriteBits(ms, 0, 1); // broken_link (0)
+
+ if(ms->error_code)
+ ezMPEG_SetError(ms, "ezMPEG_WriteGOPHeader: Couldn't write GOP header");
+
+ return;
+}
+
+void ezMPEG_WritePictureHeader(ezMPEGStream *ms)
+{
+ ezMPEG_ByteAlign(ms);
+ ezMPEG_WriteBits(ms, 0x100, 32); // picture_start_code (0x100)
+
+ // temporal_reference (0) wird bei jedem Bild in dieser GOP um 1 erhöht
+ // und fängt bei einer neuen GOP wieder bei 0 an
+ ezMPEG_WriteBits(ms, ms->picture_count % ms->gop_size, 10);
+
+ ezMPEG_WriteBits(ms, 1, 3); // picture_coding_type (1, I-Picture)
+ ezMPEG_WriteBits(ms, 0, 16); // vbv_delay (0, kann irgendwas sein wenn variable Bitrate)
+
+ ezMPEG_WriteBits(ms, 0, 1); // extra_bit_picture
+
+ // Ein Slice pro Picture sollte reichen
+ ezMPEG_ByteAlign(ms);
+ ezMPEG_WriteBits(ms, 0x101, 32); // slice_start_code (vertikale Position diese Slices in Macroblockeinheiten)
+ // horizontale Position gegeben durch macroblock_address(_increment) des ersten
+ // Macroblocks in diesem Slice. Jeder Slice ist 16 Pels hoch
+ // Reset der DC-Keoff. für differential-Codierung
+ // dc_lum_prev = 128, dc_cb_prev = 128, dc_cr_prev = 128
+ // slice_vertical_position = macroblock_row + 1;
+
+ // quantize_scale, in jedem Macroblockheader muss macroblock_type=intra_q (01), oder intra_d (1) sein.
+ // Wenn intra_q => macroblock_quant wird == 1 sein
+ ezMPEG_WriteBits(ms, (int)ms->q_scale, 5);
+
+ ezMPEG_WriteBits(ms, 0, 1); // extra_bit_slice
+
+ ms->dc_prev[0] = 128;
+ ms->dc_prev[1] = 128;
+ ms->dc_prev[2] = 128;
+
+ if(ms->error_code)
+ ezMPEG_SetError(ms, "ezMPEG_WritePictureHeader: Couldn't write picture header");
+
+ return;
+}
+
+Macroblock ezMPEG_GetMacroblock(ezMPEGStream *ms, unsigned char *picture, int number)
+{
+ int i, j, k, l, m;
+ int mb_r[64], mb_g[64], mb_b[64];
+ int offset;
+ Macroblock mb;
+
+ i = number / ms->hsize;
+ j = number % ms->hsize;
+ offset = 3 * ms->hsize * 16;
+
+ // 1. Block
+ m = 0;
+ for(k = i * 16; k < i * 16 + 8; k++) {
+ for(l = j * 16 * 3; l < j * 16 * 3 + 24; l += 3) {
+ mb.lum[0].a[m] = 0.299 * (float)picture[k * offset + l] + 0.587 * (float)picture[k * offset + l + 1] + 0.114 * (float)picture[k * offset + l + 2];
+
+ mb_r[m] = picture[k * offset + l];
+ mb_g[m] = picture[k * offset + l + 1];
+ mb_b[m] = picture[k * offset + l + 2];
+
+ m++;
+ }
+ }
+
+ // 2. Block
+ m = 0;
+ for(k = i * 16; k < i * 16 + 8; k++) {
+ for(l = j * 16 * 3 + 24; l < j * 16 * 3 + 48; l += 3) {
+ mb.lum[1].a[m] = 0.299 * (float)picture[k * offset + l] + 0.587 * (float)picture[k * offset + l + 1] + 0.114 * (float)picture[k * offset + l + 2];
+
+ mb_r[m] += picture[k * offset + l];
+ mb_g[m] += picture[k * offset + l + 1];
+ mb_b[m] += picture[k * offset + l + 2];
+
+ m++;
+ }
+ }
+
+ // 3. Block
+ m = 0;
+ for(k = i * 16 + 8; k < (i + 1) * 16; k++) {
+ for(l = j * 16 * 3; l < j * 16 * 3 + 24; l += 3) {
+ mb.lum[2].a[m] = 0.299 * (float)picture[k * offset + l] + 0.587 * (float)picture[k * offset + l + 1] + 0.114 * (float)picture[k * offset + l + 2];
+
+ mb_r[m] += picture[k * offset + l];
+ mb_g[m] += picture[k * offset + l + 1];
+ mb_b[m] += picture[k * offset + l + 2];
+
+ m++;
+ }
+ }
+
+ // 4. Block
+ m = 0;
+ for(k = i * 16 + 8; k < (i + 1) * 16; k++) {
+ for(l = j * 16 * 3 + 24; l < j * 16 * 3 + 48; l += 3) {
+ mb.lum[3].a[m] = 0.299 * (float)picture[k * offset + l] + 0.587 * (float)picture[k * offset + l + 1] + 0.114 * (float)picture[k * offset + l + 2];
+
+ mb_r[m] += picture[k * offset + l];
+ mb_g[m] += picture[k * offset + l + 1];
+ mb_b[m] += picture[k * offset + l + 2];
+
+ m++;
+ }
+ }
+
+ for(m = 0; m < 64; m++) {
+ mb.chrom[0].a[m] = -0.1687 * (float)mb_r[m] * 0.25 - 0.3313 * (float)mb_g[m] * 0.25 + 0.5 * (float)mb_b[m] * 0.25 + 128.0;
+ mb.chrom[1].a[m] = 0.5 * (float)mb_r[m] * 0.25 - 0.4187 * (float)mb_g[m] * 0.25 - 0.0813 * (float)mb_b[m] * 0.25 + 128.0;
+ }
+
+ return mb;
+}
+
+Macroblock ezMPEG_QuantizeMacroblock(ezMPEGStream *ms, Macroblock mb)
+{
+ int i, j, l, p;
+ float w[8];
+ Macroblock tempmb;
+
+ // Ueber alle Luminanceblocks gehen
+ for(l = 0; l < 4; l++) {
+ // Ueber alle Einträge im Block gehen
+ for(i = 0; i < 8; i++) {
+ for(j = 0; j < 8; j++) {
+
+ for(p = 0; p < 8; p++) {
+ // Koeffizienten mit FDCT berechnen
+ w[p] = ezMPEG_1DFDCT(&mb.lum[l].a[p * 8], j);
+ }
+
+ tempmb.lum[l].a[i * 8 + j] = ezMPEG_1DFDCT(w, i) * c(i) * c(j);
+
+ // Quantifizierung
+ if(i == 0 && j == 0)
+ tempmb.lum[l].a[i * 8 + j] /= 8.0;
+ else // mit quantizer_scale (slice_header und macroblock_header beachten)
+ tempmb.lum[l].a[i * 8 + j] *= 8.0 / (ms->q_scale * intraquant[i * 8 + j]);
+ }
+ }
+ }
+
+ // Ueber alle Chrominanceblocks gehen
+ for(l = 0; l < 2; l++) {
+ // Ueber alle Einträge im Block gehen
+ for(i = 0; i < 8; i++) {
+ for(j = 0; j < 8; j++) {
+
+ for(p = 0; p < 8; p++) {
+ // Koeffizienten mit FDCT berechnen
+ w[p] = ezMPEG_1DFDCT(&mb.chrom[l].a[p * 8], j);
+ }
+
+ tempmb.chrom[l].a[i * 8 + j] = ezMPEG_1DFDCT(w, i) * c(i) * c(j);
+
+ // Quantifizierung
+ if(i == 0 && j == 0)
+ tempmb.chrom[l].a[i * 8 + j] /= 8.0;
+ else // mit quantizer_scale
+ tempmb.chrom[l].a[i * 8 + j] *= 8.0 / (ms->q_scale * intraquant[i * 8 + j]);
+ }
+ }
+ }
+
+ return tempmb;
+}
+
+float ezMPEG_1DFDCT(float *v, int i)
+{
+ int n;
+ float temp;
+
+ temp = 0.0;
+ if((i % 2)) { // i ungerade
+ for(n = 0; n < 4; n++)
+ temp += (v[n] - v[7 - n]) * fcostable[i * 4 + n];
+ }
+ else { // i gerade
+ for(n = 0; n < 4; n++)
+ temp += (v[n] + v[7 - n]) * fcostable[i * 4 + n];
+ }
+
+ return temp;
+}
+
+void ezMPEG_EncodeAC(ezMPEGStream *ms, int runlength, int level)
+{
+ int level_abs;
+
+ level_abs = level < 0 ? -level : level;
+
+ // ac_codes_intra[runlength][0]: Dieser Wert dort gibt an, ob der entsprechende Level
+ // direkt codiert werden kann:
+ if((level_abs > ac_codes_intra[runlength][0]) || runlength > 31) {
+
+ // Benutze ac_codes_special[BLOCK_ESCAPE]
+ ezMPEG_WriteBits(ms, ac_codes_special[BLOCK_ESCAPE], ac_length_special[BLOCK_ESCAPE]);
+
+ // Table D.16, Codierung der Runlenght, wenn runlength/level-Kombination in Tabel D.15 nicht vorkommt:
+ ezMPEG_WriteBits(ms, runlength, 6);
+
+ // Der Escape-Code wurde oben schon ausgegeben und es ist kein Array nötig, da die runlength direkt codiert wird
+
+ // Hier kommet noch Tabelle D.17 zur Anwendung
+ // Hier muss noch der Level codiert werden:
+
+ if(level > 0) {
+ if(level >= 1 && level < 128)
+ ezMPEG_WriteBits(ms, level, 8);
+ else
+ ezMPEG_WriteBits(ms, level, 16);
+ }
+ else {
+ if(level <= -1 && level > -128) {
+ ezMPEG_WriteBits(ms, 256 + level, 8);
+ }
+ else {
+ ezMPEG_WriteBits(ms, 32896 + (level + 128), 16);
+ }
+ }
+ }
+ else {
+ // Table D.15
+ // code = ac_codes_intra[runlength][abs(level)];
+ // length = ac_length_intra[runlength][abs(level)];
+ ezMPEG_WriteBits(ms, ac_codes_intra[runlength][level_abs], ac_length_intra[runlength][level_abs]);
+
+ // Und noch das Vorzeichen
+ if(level < 0.0) {
+ ezMPEG_WriteBits(ms, 1, 1);
+ }
+ else {
+ ezMPEG_WriteBits(ms, 0, 1);
+ }
+ }
+
+ if(ms->error_code)
+ ezMPEG_SetError(ms, "ezMPEG_EncodeAC: Couldn't write ac codes");
+
+ return;
+}
+
+void ezMPEG_EncodeDC(ezMPEGStream *ms, int dc_diff, int type)
+{
+ int dc_diff_abs;
+ int code = 0, length = 0;
+
+ dc_diff_abs = dc_diff < 0 ? -dc_diff : dc_diff;
+
+ if(type == LUMINANCE) {
+ if(dc_diff_abs == 0) {
+ code = diff_dc_size_codes_lum[0];
+ length = diff_dc_size_length_lum[0];
+ }
+ else if(dc_diff_abs == 1) {
+ code = diff_dc_size_codes_lum[1];
+ length = diff_dc_size_length_lum[1];
+ }
+ else if(dc_diff_abs >= 2 && dc_diff_abs <= 3) {
+ code = diff_dc_size_codes_lum[2];
+ length = diff_dc_size_length_lum[2];
+ }
+ else if(dc_diff_abs >= 4 && dc_diff_abs <= 7) {
+ code = diff_dc_size_codes_lum[3];
+ length = diff_dc_size_length_lum[3];
+ }
+ else if(dc_diff_abs >= 8 && dc_diff_abs <= 15) {
+ code = diff_dc_size_codes_lum[4];
+ length = diff_dc_size_length_lum[4];
+ }
+ else if(dc_diff_abs >= 16 && dc_diff_abs <= 31) {
+ code = diff_dc_size_codes_lum[5];
+ length = diff_dc_size_length_lum[5];
+ }
+ else if(dc_diff_abs >= 32 && dc_diff_abs <= 63) {
+ code = diff_dc_size_codes_lum[6];
+ length = diff_dc_size_length_lum[6];
+ }
+ else if(dc_diff_abs >= 64 && dc_diff_abs <= 127) {
+ code = diff_dc_size_codes_lum[7];
+ length = diff_dc_size_length_lum[7];
+ }
+ else if(dc_diff_abs >= 128 && dc_diff_abs <= 255) {
+ code = diff_dc_size_codes_lum[8];
+ length = diff_dc_size_length_lum[8];
+ }
+ else {
+ ezMPEG_SetError(ms, "ezMPEG_EncodeDC: FATAL! absolute dc difference > 255 (Luminance)");
+ }
+ }
+ else if(type == CHROMINANCE) {
+ if(dc_diff_abs == 0) {
+ code = diff_dc_size_codes_chrom[0];
+ length = diff_dc_size_length_chrom[0];
+ }
+ else if(dc_diff_abs == 1) {
+ code = diff_dc_size_codes_chrom[1];
+ length = diff_dc_size_length_chrom[1];
+ }
+ else if(dc_diff_abs >= 2 && dc_diff_abs <= 3) {
+ code = diff_dc_size_codes_chrom[2];
+ length = diff_dc_size_length_chrom[2];
+ }
+ else if(dc_diff_abs >= 4 && dc_diff_abs <= 7) {
+ code = diff_dc_size_codes_chrom[3];
+ length = diff_dc_size_length_chrom[3];
+ }
+ else if(dc_diff_abs >= 8 && dc_diff_abs <= 15) {
+ code = diff_dc_size_codes_chrom[4];
+ length = diff_dc_size_length_chrom[4];
+ }
+ else if(dc_diff_abs >= 16 && dc_diff_abs <= 31) {
+ code = diff_dc_size_codes_chrom[5];
+ length = diff_dc_size_length_chrom[5];
+ }
+ else if(dc_diff_abs >= 32 && dc_diff_abs <= 63) {
+ code = diff_dc_size_codes_chrom[6];
+ length = diff_dc_size_length_chrom[6];
+ }
+ else if(dc_diff_abs >= 64 && dc_diff_abs <= 127) {
+ code = diff_dc_size_codes_chrom[7];
+ length = diff_dc_size_length_chrom[7];
+ }
+ else if(dc_diff_abs >= 128 && dc_diff_abs <= 255) {
+ code = diff_dc_size_codes_chrom[8];
+ length = diff_dc_size_length_chrom[8];
+ }
+ else {
+ ezMPEG_SetError(ms, "ezMPEG_EncodeDC: FATAL! absolute dc difference > 255 (Chrominance)");
+ }
+ }
+ else {
+ ezMPEG_SetError(ms, "ezMPEG_EncodeDC: FATAL! Unknown coefficient type");
+ }
+
+ ezMPEG_WriteBits(ms, code, length);
+
+ if(dc_diff == 0) {
+ return;
+ }
+
+ if(dc_diff < 0) {
+ if(dc_diff == -1) {
+ code = diff_dc_add_codes_neg[1];
+ length = diff_dc_add_length[1];
+ }
+ else if(dc_diff <= -2 && dc_diff >= -3) {
+ code = diff_dc_add_codes_neg[2] - (-2 - dc_diff);
+ length = diff_dc_add_length[2];
+ }
+ else if(dc_diff <= -4 && dc_diff >= -7) {
+ code = diff_dc_add_codes_neg[3] - (-4 - dc_diff);
+ length = diff_dc_add_length[3];
+ }
+ else if(dc_diff <= -8 && dc_diff >= -15) {
+ code = diff_dc_add_codes_neg[4] - (-8 - dc_diff);
+ length = diff_dc_add_length[4];
+ }
+ else if(dc_diff <= -16 && dc_diff >= -31) {
+ code = diff_dc_add_codes_neg[5] - (-16 - dc_diff);
+ length = diff_dc_add_length[5];
+ }
+ else if(dc_diff <= -32 && dc_diff >= -63) {
+ code = diff_dc_add_codes_neg[6] - (-32 - dc_diff);
+ length = diff_dc_add_length[6];
+ }
+ else if(dc_diff <= -64 && dc_diff >= -127) {
+ code = diff_dc_add_codes_neg[7] - (-64 - dc_diff);
+ length = diff_dc_add_length[7];
+ }
+ else if(dc_diff <= -128 && dc_diff >= -255) {
+ code = diff_dc_add_codes_neg[8] - (-128 - dc_diff);
+ length = diff_dc_add_length[8];
+ }
+ else {
+ ezMPEG_SetError(ms, "ezMPEG_EncodeDC: FATAL! dc difference < -255");
+ }
+ }
+ else {
+ if(dc_diff == 1) {
+ code = diff_dc_add_codes_pos[1];
+ length = diff_dc_add_length[1];
+ }
+ else if(dc_diff >= 2 && dc_diff <= 3) {
+ code = diff_dc_add_codes_pos[2] + (dc_diff - 2);
+ length = diff_dc_add_length[2];
+ }
+ else if(dc_diff >= 4 && dc_diff <= 7) {
+ code = diff_dc_add_codes_pos[3] + (dc_diff - 4);
+ length = diff_dc_add_length[3];
+ }
+ else if(dc_diff >= 8 && dc_diff <= 15) {
+ code = diff_dc_add_codes_pos[4] + (dc_diff - 8);
+ length = diff_dc_add_length[4];
+ }
+ else if(dc_diff >= 16 && dc_diff <= 31) {
+ code = diff_dc_add_codes_pos[5] + (dc_diff - 16);
+ length = diff_dc_add_length[5];
+ }
+ else if(dc_diff >= 32 && dc_diff <= 63) {
+ code = diff_dc_add_codes_pos[6] + (dc_diff - 32);
+ length = diff_dc_add_length[6];
+ }
+ else if(dc_diff >= 64 && dc_diff <= 127) {
+ code = diff_dc_add_codes_pos[7] + (dc_diff - 64);
+ length = diff_dc_add_length[7];
+ }
+ else if(dc_diff >= 128 && dc_diff <= 255) {
+ code = diff_dc_add_codes_pos[8] + (dc_diff - 128);
+ length = diff_dc_add_length[8];
+ }
+ else {
+ ezMPEG_SetError(ms, "ezMPEG_EncodeDC: FATAL! dc difference > 255");
+ }
+ }
+
+ ezMPEG_WriteBits(ms, code, length);
+
+ return;
+}
+
+int ezMPEG_Finalize(ezMPEGStream *ms)
+{
+ // Den Stream mit dem entsprechenden Code beenden
+ // und den Buffer flushen
+ ezMPEG_ByteAlign(ms);
+ ezMPEG_WriteBits(ms, 0x01B7, 32); // sequence_end_code
+
+ ezMPEG_FlushBuffer(ms);
+
+ // ms->out schliessen
+ fclose(ms->out);
+
+ // Speicher freigeben
+ free(ms->outfile);
+ free(ms->buffer);
+
+ if(ms->error_code)
+ return 0;
+
+ return 1;
+}
+
+void ezMPEG_FlushBuffer(ezMPEGStream *ms)
+{
+ if(fwrite(ms->buffer, ms->buffercount, 1, ms->out) < 1)
+ ezMPEG_SetError(ms, "ezMPEG_FlushBuffer: Couldn't write buffer to file");
+
+ ms->buffercount = 0;
+
+ return;
+}
+
+void ezMPEG_SetError(ezMPEGStream *ms, const char *error_msg)
+{
+ if(ms != NULL && error_msg != NULL) {
+ ms->error_code = 1;
+ strcpy(ms->error_msg, error_msg);
+ }
+
+ return;
+}
+
+char *ezMPEG_GetLastError(ezMPEGStream *ms)
+{
+ ms->error_code = 0;
+ return ms->error_msg;
+}
+
+void ezMPEG_Resize(ezMPEGStream *ms, unsigned char *outbild, unsigned char *inbild, int x, int y, int u, int v)
+{
+ float hor, ver;
+ int a, b, c, d, e, f, g, h;
+
+ if(outbild == NULL || inbild == NULL)
+ ezMPEG_SetError(ms, "ezMPEG_Resize: Nullpointer");
+
+ if(x <= 0 || y <= 0 || u <= 0 || v <= 0)
+ ezMPEG_SetError(ms, "ezMPEG_Resize: Dimensions must be greater than 0");
+
+ if(ms->error_code)
+ return;
+
+ hor = (float)u / (float)x;
+ ver = (float)v / (float)y;
+
+ c = y;
+ d = x;
+
+ if(ver > 1.0) {
+ c = v;
+ }
+ if(hor > 1.0) {
+ d = u;
+ }
+
+
+ for(a = 0; a < c; a++) {
+ for(b = 0; b < d; b++) {
+ if(ver > 1.0) {
+ e = a * u;
+ g = (int)((float)a / ver) * x;
+ }
+ else {
+ e = (int)((float)a * ver) * u;
+ g = a * x;
+ }
+
+ if(hor > 1.0) {
+ f = b;
+ h = (int)((float)b / hor);
+ }
+ else {
+ f = (int)((float)b * hor);
+ h = b;
+ }
+
+ outbild[e * 3 + f * 3] = inbild[g * 3 + h * 3];
+ outbild[e * 3 + f * 3 + 1] = inbild[g * 3 + h * 3 + 1];
+ outbild[e * 3 + f * 3 + 2] = inbild[g * 3 + h * 3 + 2];
+ }
+ }
+
+ return;
+}