[xiph-commits] r6933 - experimental/giles

giles at dactyl.lonelymoon.com giles
Tue Jun 29 22:07:59 PDT 2004


Author: giles
Date: Tue Jun 29 22:07:59 2004
New Revision: 6933

Added:
experimental/giles/avidump.c
Log:
This is a little util I wrote to losslessly convert the mjpeg AVI files
my digital camera makes to Ogg MNG+FLAC. Currently it extracts .wav
audio successfully, and stand-along MNG unsuccessfully.

Committing to avoid losing work.


Added: experimental/giles/avidump.c
===================================================================
--- experimental/giles/avidump.c	2004-06-30 03:51:29 UTC (rev 6932)
+++ experimental/giles/avidump.c	2004-06-30 05:07:56 UTC (rev 6933)
@@ -0,0 +1,617 @@
+/*
+   avidump.c
+
+   simple format dump utility for avi movies
+
+   Copyright (C) 2004 Ralph Giles. All rights reserved.
+
+   Distributed under the terms of the GNU GPL.
+   See http://www.gnu.org/ for details.
+*/
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+FILE *wav;
+FILE *mng;
+int mng_width, mng_height;
+int mng_fps;
+
+#undef MIN
+#define MIN(x,y) (((x)>(y))?(y):(x))
+
+/* read a 16 bit little-endian integer */
+void read16(FILE *in, uint16_t *p)
+{
+	unsigned char q[2];
+	fread(q, 1, 2, in);
+	*p = q[0] | q[1] << 8;
+	return;
+}
+
+/* read a 32 bit little-endian integer */
+void read32(FILE *in, uint32_t *p)
+{
+	unsigned char q[4];
+	fread(q, 1, 4, in);
+	*p = q[0] | q[1] << 8 | q[2] << 16 | q[3] << 24;
+	return;
+}
+
+/* write a 16 bit little-endian integer */
+void write16(FILE *out, uint16_t p)
+{
+	unsigned char q[2];
+	q[0] = p & 0xFF;
+	q[1] = (p >> 8) & 0xFF;
+	fwrite(q, 1, 2, out);
+	return;
+}
+
+/* write an 8 bit integer */
+void write8(FILE *out, unsigned char p)
+{
+	fwrite(&p, 1, 1, out);
+	return;
+}
+
+/* write a 32 bit little-endian integer */
+void write32(FILE *out, uint32_t p)
+{
+	unsigned char q[4];
+	q[0] = p & 0xFF;
+	q[1] = (p >> 8) & 0xFF;
+	q[2] = (p >> 16) & 0xFF;
+	q[3] = (p >> 24) & 0xFF;
+	fwrite(q, 1, 4, out);
+	return;
+}
+
+/* write a 32 bit big-endian integer */
+void write_be32(FILE *out, uint32_t p)
+{
+	unsigned char q[4];
+	q[0] = (p >> 24) & 0xFF;
+	q[1] = (p >> 16) & 0xFF;
+	q[2] = (p >> 8) & 0xFF;
+	q[3] = p & 0xFF;
+	fwrite(q, 1, 4, out);
+	return;
+}
+
+/* insert a 32 bit big-endian integer into a buffer */
+void put_be32(unsigned char *p, uint32_t q)
+{
+	p[0] = (q >> 24) & 0xFF;
+	p[1] = (q >> 16) & 0xFF;
+	p[2] = (q >> 8) & 0xFF;
+	p[3] = q & 0xFF;
+	return;
+}
+
+/* fix the length fields of a wav file */
+int wav_finalize(FILE *f)
+{
+	unsigned long size = ftell(f);
+	int status = 0;
+
+	/* set the file length */
+	status = fseek(f, 4, SEEK_SET);
+	write32(f, size - 8);
+	/* set data length */
+	status = fseek(f, 40, SEEK_SET);
+	write32(f, size - 44);
+
+	return status;
+}
+
+/* write the crc for a mng chunk
+   assuming we've just finished writing the body */
+uint32_t mng_update_crc(uint32_t crc, unsigned char *buf, uint32_t len)
+{
+	static uint32_t table[256]; /* speed up table of 8 bit crcs */
+	static int table_computed = 0; /* remember if table has been computed */
+	int i;
+
+	/* precompute a table of 8 bit crc results */
+	if (!table_computed) {
+		uint32_t c;
+		int k;
+		for (i = 0; i < 256; i++) {
+			c = (uint32_t)i;
+			for (k = 0; k < 8; k++) {
+				if (c & 1)
+					c = 0xedb88320 ^ (c >> 1);
+				else
+					c = c >> 1;
+			}
+			table[i] = c;
+		}
+		table_computed = 1;
+	}
+
+	/* run our buffer through the crc one byte at a time */
+	for (i=0; i < len; i++) {
+		crc = table[(crc ^ *buf++) & 0xFF] ^ (crc >> 8);
+	}
+
+	return crc;
+}
+
+/* write the MNG headers for a VLC+JNG stream */
+int mng_init(FILE *f, int width, int height, int fps)
+{
+	const unsigned char sig[8] = {'\212','M','N','G','\r','\n','\032','\n'};
+	unsigned char buf[48];
+	unsigned char *p;
+	uint32_t crc;
+
+	/* record the dimensions */
+	mng_width = width;
+	mng_height = height;
+
+	memcpy(buf, sig, 8); /* MNG file signature */
+
+	/* MHDR */
+	p = buf + 8;
+	put_be32(p, 28); /* body length */
+	memcpy(p+4, "MHDR", 4); /* chunk type */
+	p += 8;
+	put_be32(p, width);
+	put_be32(p+4, height);
+	put_be32(p+8, fps);
+	put_be32(p+12, 0); /* nominal layer count */
+	put_be32(p+16, 0); /* nominal frame count */
+	put_be32(p+20, 0); /* nominal play time */
+	put_be32(p+24, 1 | 1 << 4 | 1 << 6); /* profile: VLC + JNG - transparency */
+	p += 28;
+	crc = mng_update_crc(0xFFFFFFFF, buf+12, 4 + 28) ^ 0xFFFFFFFF;
+	fprintf(stderr, "writing %c%c%c%c crc 0x%08x\n",
+		buf[12],buf[13],buf[14],buf[15], crc);
+	put_be32(p, crc);
+
+	/* write out the data */
+	fwrite(buf, 1, 48, f);
+
+	return 0;
+}
+
+/* copy a frame into a JNG datastream */
+int mng_copy_jng(FILE *f, FILE *in, int len)
+{
+	unsigned char *buf;
+	int s, m, read = 0;
+	uint32_t crc;
+
+	/* JHDR */
+	s = 4*4;
+	buf = malloc(s + 12);
+	put_be32(buf, s);
+	memcpy(buf+4, "JHDR", 4);
+	put_be32(buf+8, mng_width);
+	put_be32(buf+8+4, mng_height);
+	buf[8+8] = 10; /* color type YCbCr */
+	buf[8+9] = 8;  /* sample and q table bits */
+	buf[8+10] = 8; /* baseline jpeg compression */
+	buf[8+11] = 0; /* not progressive jpeg */
+	put_be32(buf+8+12, 0); /* no alpha layer */
+	crc = mng_update_crc(0xFFFFFFFF, buf + 4, s + 4) ^ 0xFFFFFFFF;
+	put_be32(buf+8+s, crc);
+	fwrite(buf, 1, s + 12, f);
+	free(buf);
+
+	/* JDAT */
+	m = MIN(0x10000,len);
+	buf = malloc(m);
+	while (read < len) {
+		s = MIN(len - read, m);
+		fread(buf, 1, s, in);
+		write_be32(f, s);   /* chunk size */
+		fprintf(f, "JDAT"); /* chunk type */
+		fwrite(buf, 1, s, f); /* chunk body */
+		crc = 0xFFFFFFFF;
+		crc = mng_update_crc(crc, "JDAT", 4);
+		crc = mng_update_crc(crc, buf, s);
+		crc ^= 0xFFFFFFFF;
+		write_be32(f, crc); /* chunk CRC */
+		read += s;
+	}
+	free(buf);
+
+	/* IEND */
+	write_be32(f, 0);   /* length */
+	fprintf(f, "IEND"); /* type */
+	crc = mng_update_crc(0xFFFFFFFF, "IEND", 4) ^ 0xFFFFFFFF;
+	write_be32(f, crc); /* CRC */
+
+	return 0;
+}
+
+
+/* finalize mng output */
+int mng_finalize(FILE *f)
+{
+	uint32_t crc;
+
+	/* MEND */
+	write_be32(f, 0);   /* length */
+	fprintf(f, "MEND"); /* type */
+	crc = mng_update_crc(0xFFFFFFFF, "MEND", 4) ^ 0xFFFFFFFF;
+	fprintf(stderr, "writing out MEND crc 0x%08x\n", crc);
+	write_be32(f, crc); /* crc */
+
+	return 0;
+}
+
+int dump_chunk_avih(const char *type, uint32_t len, FILE *in, FILE *out)
+{
+	uint32_t period, rate;
+	uint32_t streams;
+	int read = 0;
+
+	read32(in, &period); /* often incorrect */
+	read32(in, &rate);   /* (nominal?) bitrate */
+	read += 8;
+        fseek(in, 4 * 4, SEEK_CUR); /* skip 4 fields */
+	read += 16;
+	read32(in, &streams);/* number of streams */
+	read += 4;
+
+	fprintf(out, "\n   %d streams, nominal period %d nominal bitrate %d",
+		streams, period, rate);
+
+	return fseek(in, len-read, SEEK_CUR);
+}
+
+int dump_chunk_strh(const char *type, uint32_t len, FILE *in, FILE *out)
+{
+	int read = 0;
+	unsigned char subtype[5], codec[5];
+
+	fread(subtype, 1, 4, in); subtype[4] = '\0';
+	fread(codec, 1, 4, in); codec[4] = '\0';
+	read += 8;
+
+	fprintf(out, " stream header");
+	fprintf(out, "\n   %s: %s", subtype, codec);
+
+	if (!strncmp(subtype, "vids", 4)) {
+		uint32_t flags;
+		uint16_t priority, language;
+		int start, count;
+		int scale, rate;
+		int starttime;
+
+		read32(in, &flags);
+		read16(in, &priority);
+		read16(in, &language);
+		read32(in, &start);
+		read32(in, &scale);
+		read32(in, &rate);
+		read32(in, &starttime);
+		read32(in, &count);
+		read += 6*4 + 2*2;
+
+		if (!rate && !scale) {
+			/* should look at period from avih if it's set */
+			fprintf(out, " framerate unspecified\n");
+		} else {
+			double fps = (double)rate/(double)scale;
+			double duration = (double)count/fps;
+			fprintf(out, " %.2fs video at %.2f fps", duration, fps);
+		}
+
+		if (!scale) scale =  1;
+		mng_fps = (int) ((double)rate/(double)scale);
+		if (!mng_fps) mng_fps = 15;
+
+		fprintf(out, "\n    flags 0x%08x", flags);
+		fprintf(out, "\n    priority %d", priority);
+		fprintf(out, "\n    language %d", language);
+		fprintf(out, "\n    start %d", start);
+		fprintf(out, "\n    scale %d", scale);
+		fprintf(out, "\n    rate %d", rate);
+		fprintf(out, "\n    starttime %d", starttime);
+		fprintf(out, "\n    count %d", count);
+	} else if (!strncmp(subtype, "auds", 4)) {
+		uint32_t flags;
+		uint16_t priority, language;
+		int initial, scale, rate, start;
+		int length, bufsize, samplesize, quality;
+
+		if (!strncmp(codec, "\0\0\0\0", 4))
+			fprintf(out, " (PCM audio)");
+		read32(in, &flags);
+		read16(in, &priority);
+		read16(in, &language);
+		read32(in, &initial);
+		read32(in, &scale);
+		read32(in, &rate);
+		read32(in, &start);
+		read32(in, &length);
+		read32(in, &bufsize);
+		read32(in, &quality);
+		read32(in, &samplesize);
+		read += 2*2 + 9*4;
+
+		fprintf(out, " %d Hz", rate/scale);
+		fprintf(out, " %d bits per sample", samplesize*8);
+
+		fprintf(out, "\n    flags 0x%08x", flags);
+		fprintf(out, "\n    priority %d", priority);
+		fprintf(out, "\n    language %d", language);
+		fprintf(out, "\n    initial %d", initial);
+		fprintf(out, "\n    scale %d", scale);
+		fprintf(out, "\n    rate %d", rate);
+		fprintf(out, "\n    start %d", start);
+		fprintf(out, "\n    length %d", length);
+		fprintf(out, "\n    bufsize %d", bufsize);
+		fprintf(out, "\n    quality %d", quality);
+		fprintf(out, "\n    samplesize %d", samplesize);
+	}
+	return fseek(in, len-read, SEEK_CUR);
+}
+
+int dump_chunk_strf(const char *type, uint32_t len, FILE *in, FILE *out)
+{
+	static int called = 0; /* we're called first for video, then for audio */
+	int read = 0;
+
+	if (!called) {
+		/* video stream format */
+		uint32_t size, width, height;
+		uint16_t panes, depth;
+		unsigned char tag[5];
+		int imagesize, xres, yres;
+
+		read32(in, &size);
+		read32(in, &width);
+		read32(in, &height);
+		read16(in, &panes);
+		read16(in, &depth);
+		fread(tag, 1, 4, in); tag[4] = '\0';
+		read32(in, &imagesize);
+		read32(in, &xres);
+		read32(in, &yres);
+		read += 8*4;
+
+		fprintf(out, " %dx%d images, %d bits per pixel",
+			width, height, depth);
+		fprintf(out, "\n    size %d", size);
+		fprintf(out, "\n    tag '%s'", tag);
+		fprintf(out, "\n    panes %d", panes);
+		fprintf(out, "\n    image size %d", imagesize);
+		fprintf(out, "\n    resolution %dx%d pixels per meter", xres, yres);
+
+		called++;
+
+		mng_init(mng, width, height, mng_fps);
+	} else {
+		/* audio stream format */
+		uint16_t format, channels;
+		int samplerate, datarate;
+		uint16_t blockalign, bits;
+
+		read16(in, &format);
+		read16(in, &channels);
+		read32(in, &samplerate);
+		read32(in, &datarate); /* bytes per second */
+		read16(in, &blockalign);
+		read16(in, &bits); /* bits per sample */
+		read += 16;
+
+		fprintf(out, " %d channel %d Hz %d bit audio",
+			channels, samplerate, bits);
+		fprintf(out, "\n    format %d", format);
+		switch (format) {
+			case 0x0001: fprintf(out, " (PCM audio)"); break;
+			case 0x0101: fprintf(out, " (muLAW)"); break;
+			case 0x0102: fprintf(out, " (IBM aLAW)"); break;
+			case 0x0103: fprintf(out, " (IBM ADPCM)"); break;
+			default: fprintf(out, " (unknown)"); break;
+		}
+		fprintf(out, " blockalign %d", blockalign);
+		fprintf(out, "\n    datarate %d bytes per second", datarate);
+
+		/* dump wav header to separate file */
+		fprintf(wav, "RIFF");
+		write32(wav, 0); /* file body length */
+		fprintf(wav, "WAVE");
+		fprintf(wav, "fmt ");
+		write32(wav, 16); /* format chunk body length */
+		write16(wav, format);
+		write16(wav, channels);
+		write32(wav, samplerate);
+		write32(wav, datarate);
+		write16(wav, blockalign);
+		write16(wav, bits);
+		fprintf(wav, "data");
+		write32(wav, 0);
+	}
+
+	return fseek(in, len-read, SEEK_CUR);
+}
+
+int dump_chunk_IDIT(const char *type, uint32_t len, FILE *in, FILE *out)
+{
+	int read = 0;
+	char *string;
+
+	string = malloc(len + 1);
+	if (string) {
+		fread(string, 1, len, in);
+		read += len;
+		string[len] = '\0'; /* seem to be null terminated, but be sure */
+		fprintf(out, " \"%s\"", string);
+		free(string);
+	}
+
+	return fseek(in, len-read, SEEK_CUR);
+}
+
+int dump_chunk_ISFT(const char *type, uint32_t len, FILE *in, FILE *out)
+{
+	int read = 0;
+	char*string;
+
+	string = malloc(len + 1);
+	if (string) {
+		fread(string, 1, len, in);
+		read += len;
+		string[len] = '\0';
+		fprintf(out, " \"%s\"", string);
+		free(string);
+	}
+
+	return fseek(in, len-read, SEEK_CUR);
+}
+
+int dump_chunk_xxwb(const char *type, uint32_t len, FILE *in, FILE *out)
+{
+	int read = 0;
+	unsigned char buf[4096];
+	int s;
+
+	fprintf(out, " audio frame data.");
+
+	/* copy data to a separate file */
+	while (read < len) {
+		s = MIN(len - read, 4096);
+		fread(buf, 1, s, in);
+		fwrite(buf, 1, s, wav);
+		read += s;
+	}
+
+	return fseek(in, len-read, SEEK_CUR);
+}
+
+int dump_chunk_xxdc(const char *type, uint32_t len, FILE *in, FILE *out)
+{
+	int read = 0;
+	fprintf(out, " video frame data.");
+
+	mng_copy_jng(mng, in, len);
+	read += len;
+
+	return fseek(in, len-read, SEEK_CUR);
+}
+
+
+int dump_chunk_xxdb(const char *type, uint32_t len, FILE *in, FILE *out)
+{
+	int read = 0;
+	fprintf(out, " uncompressed bitmap data.");
+	return fseek(in, len-read, SEEK_CUR);
+}
+
+int dump_chunk(const char* type, FILE *in, FILE *out)
+{
+	uint32_t len;
+	int status = 0;
+
+	read32(in, &len);
+	fprintf(out, "  %s (%d bytes)", type, len);
+	if (!strncmp(type+2, "wb", 2))
+		status = dump_chunk_xxwb(type, len, in, out);
+	else if (!strncmp(type+2, "dc", 2))
+		status = dump_chunk_xxdc(type, len, in, out);
+	else if (!strncmp(type+2, "db", 2))
+		status = dump_chunk_xxdb(type, len, in, out);
+	else if (!strncmp(type, "avih", 4))
+		status = dump_chunk_avih(type, len, in,out);
+	else if (!strncmp(type, "strh", 4))
+		status = dump_chunk_strh(type, len, in,out);
+	else if (!strncmp(type, "strf", 4))
+		status = dump_chunk_strf(type, len, in,out);
+	else if (!strncmp(type, "IDIT", 4))
+		status = dump_chunk_IDIT(type, len, in,out);
+	else if (!strncmp(type, "ISFT", 4))
+		status = dump_chunk_ISFT(type, len, in,out);
+	else
+		/* skip over chunk body data */
+		status = fseek(in, len, SEEK_CUR);
+	fprintf(out, "\n");
+
+	return status;
+}
+
+int dump_list(FILE *in, FILE *out)
+{
+	unsigned char type[5];
+	uint32_t len;
+
+	read32(in, &len);
+	fread(type, 1, 4, in); type[4] = '\0';
+	fprintf(out, " LIST %s (%d bytes):\n", type, len);
+	return 0;
+}
+
+int dispatch(FILE *in, FILE *out)
+{
+	unsigned char sig[5];
+	int read;
+
+	read = fread(sig, 1, 4, in); sig[4] = '\0';
+	if (!read) return 0;
+	if (read != 4) return 1;
+	if (!strncmp(sig, "LIST", 4)) dump_list(in, out);
+	else dump_chunk(sig, in, out);
+	return 0;
+}
+
+int dump_avi(FILE *in, FILE *out)
+{
+	char riff[4],fmt[5];
+	uint32_t len;
+	int status;
+
+	fread(riff, 1, 4, in);
+	if (strncmp(riff, "RIFF", 4)) return 1;
+	read32(in, &len);
+	fread(fmt, 1, 4, in); fmt[4] = '\0';
+	fprintf(out, "RIFF %s (%d bytes)\n", fmt, len);
+
+	while (!feof(in)) {
+		status = dispatch(in, out);
+		if (status) return status;
+	}
+
+	return 0;
+}
+
+void usage(const char *name, FILE *out)
+{
+	fprintf(out, "usage: %s <file1.avi> [<file2.avi> ...]\n", name);
+	fprintf(out, "  dumps the structure of an AVI file for inspection.\n");
+	return;
+}
+
+int main(int argc, char *argv[])
+{
+	int i;
+	FILE *in;
+
+	if (argc < 2) usage(argv[0], stderr);
+
+	for (i = 1; i < argc; i++) {
+		in = fopen(argv[1], "rb");
+		if (in != NULL) {
+			wav = fopen("out.wav", "wb");
+			mng = fopen("out.mng", "wb");
+			fprintf(stdout, "dump of '%s':\n", argv[i]);
+			if (dump_avi(in,stdout))
+				fprintf(stderr, "error parsing '%s'\n", argv[i]);
+			fclose(in);
+			wav_finalize(wav);
+			fclose(wav);
+			mng_finalize(mng);
+			fclose(mng);
+		} else {
+			fprintf(stderr, "could not open '%s'\n", argv[i]);
+		}
+	}
+
+	return 0;
+}



More information about the commits mailing list