/*
* avilib.c
*
* Copyright (C) Thomas streich - June 2001
* multiple audio track support Copyright (C) 2002 Thomas streich
*
* Original code:
* Copyright (C) 1999 Rainer Johanni <Rainer@Johanni.de>
*
* This file is part of transcode, a linux video stream processing tool
*
* transcode 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, or (at your option)
* any later version.
*
* transcode 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.
*
* You should have received a copy of the GNU General Public License
* along with GNU Make; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
//SLM
#ifdef WIN32
#include <io.h>
#define ftruncate _chsize
#define strncasecmp _strnicmp
typedef int ssize_t;
#endif
#ifdef __CYGWIN__
#include <unistd.h>
#endif
#include "avilib.h"
//#include <time.h>
#define INFO_LIST
/* The following variable indicates the kind of error */
long AVI_errno = 0;
#define MAX_INFO_STRLEN 64
static char id_str[MAX_INFO_STRLEN];
#define FRAME_RATE_SCALE 1000000
/*******************************************************************
* *
* Utilities for writing an AVI File *
* *
*******************************************************************/
static size_t avi_read(int fd, char *buf, size_t len)
{
size_t n = 0;
size_t r = 0;
while (r < len) {
n = read (fd, buf + r, len - r);
if ((ssize_t)n <= 0)
return r;
r += n;
}
return r;
}
static size_t avi_write (int fd, char *buf, size_t len)
{
size_t n = 0;
size_t r = 0;
while (r < len) {
n = write (fd, buf + r, len - r);
if ((ssize_t)n < 0)
return n;
r += n;
}
return r;
}
/* HEADERBYTES: The number of bytes to reserve for the header */
#define HEADERBYTES 2048
/* AVI_MAX_LEN: The maximum length of an AVI file, we stay a bit below
the 2GB limit (Remember: 2*10^9 is smaller than 2 GB) */
#define AVI_MAX_LEN (UINT_MAX-(1<<20)*16-HEADERBYTES)
#define PAD_EVEN(x) ( ((x)+1) & ~1 )
/* Copy n into dst as a 4 byte, little endian number.
Should also work on big endian machines */
static void long2str(unsigned char *dst, int n)
{
dst[0] = (n )&0xff;
dst[1] = (n>> 8)&0xff;
dst[2] = (n>>16)&0xff;
dst[3] = (n>>24)&0xff;
}
/* Convert a string of 4 or 2 bytes to a number,
also working on big endian machines */
static unsigned long str2ulong(unsigned char *str)
{
return ( str[0] | (str[1]<<8) | (str[2]<<16) | (str[3]<<24) );
}
static unsigned long str2ushort(unsigned char *str)
{
return ( str[0] | (str[1]<<8) );
}
/* Calculate audio sample size from number of bits and number of channels.
This may have to be adjusted for eg. 12 bits and stereo */
static int avi_sampsize(avi_t *AVI, int j)
{
int s;
s = ((AVI->track[j].a_bits+7)/8)*AVI->track[j].a_chans;
// if(s==0) s=1; /* avoid possible zero divisions */
if(s<4) s=4; /* avoid possible zero divisions */
return s;
}
/* Add a chunk (=tag and data) to the AVI file,
returns -1 on write error, 0 on success */
static int avi_add_chunk(avi_t *AVI, unsigned char *tag, unsigned char *data, int length)
{
unsigned char c[8];
/* Copy tag and length int c, so that we need only 1 write system call
for these two values */
memcpy(c,tag,4);
long2str(c+4,length);
/* Output tag, length and data, restore previous position
if the write fails */
length = PAD_EVEN(length);
if( avi_write(AVI->fdes,(char *)c,8) != 8 ||
avi_write(AVI->fdes,(char *)data,length) != length )
{
lseek(AVI->fdes,AVI->pos,SEEK_SET);
AVI_errno = AVI_ERR_WRITE;
return -1;
}
/* Update file position */
AVI->pos += 8 + length;
//fprintf(stderr, "pos=%lu %s\n", AVI->pos, tag);
return 0;
}
static int avi_add_index_entry(avi_t *AVI, unsigned char *tag, long flags, unsigned long pos, unsigned long len)
{
void *ptr;
if(AVI->n_idx>=AVI->max_idx) {
ptr = realloc((void *)AVI->idx,(AVI->max_idx+4096)*16);
if(ptr == 0) {
AVI_errno = AVI_ERR_NO_MEM;
return -1;
}
AVI->max_idx += 4096;
AVI->idx = (unsigned char((*)[16]) ) ptr;
}
/* Add index entry */
// fprintf(stderr, "INDEX %s %ld %lu %lu\n", tag, flags, pos, len);
memcpy(AVI->idx[AVI->n_idx],tag,4);
long2str(AVI->idx[AVI->n_idx]+ 4,flags);
long2str(AVI->idx[AVI->n_idx]+ 8, pos);
long2str(AVI->idx[AVI->n_idx]+12, len);
/* Update counter */
AVI->n_idx++;
if(len>AVI->max_len) AVI->max_len=len;
return 0;
}
//SLM
#ifndef S_IRUSR
#define S_IRWXU 00700 /* read, write, execute: owner */
#define S_IRUSR 00400 /* read permission: owner */
#define S_IWUSR 00200 /* write permission: owner */
#define S_IXUSR 00100 /* execute permission: owner */
#define S_IRWXG 00070 /* read, write, execute: group */
#define S_IRGRP 00040 /* read permission: group */
#define S_IWGRP 00020 /* write permission: group */
#define S_IXGRP 00010 /* execute permission: group */
#define S_IRWXO 00007 /* read, write, execute: other */
#define S_IROTH 00004 /* read permission: other */
#define S_IWOTH 00002 /* write permission: other */
#define S_IXOTH 00001 /* execute permission: other */
#endif
/*
AVI_open_output_file: Open an AVI File and write a bunch
of zero bytes as space for the header.
returns a pointer to avi_t on success, a zero pointer on error
*/
avi_t* AVI_open_output_file(char * filename)
{
avi_t *AVI;
int i;
int mask;
unsigned char AVI_header[HEADERBYTES];
/* Allocate the avi_t struct and zero it */
AVI = (avi_t *) malloc(sizeof(avi_t));
if(AVI==0)
{
AVI_errno = AVI_ERR_NO_MEM;
return 0;
}
memset((void *)AVI,0,sizeof(avi_t));
/* Since Linux needs a long time when deleting big files,
we do not truncate the file when we open it.
Instead it is truncated when the AVI file is closed */
mask = umask (0);
umask (mask);
#ifdef WIN32
AVI->fdes = open(filename, O_RDWR|O_CREAT|O_BINARY, (S_IRUSR | S_IWUSR) &~ mask);
#else
AVI->fdes = open(filename, O_RDWR|O_CREAT, (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) &~ mask);
#endif
if (AVI->fdes < 0)
{
AVI_errno = AVI_ERR_OPEN;
free(AVI);
return 0;
}
/* Write out HEADERBYTES bytes, the header will go here
when we are finished with writing */
for (i=0;i<HEADERBYTES;i++) AVI_header[i] = 0;
i = avi_write(AVI->fdes,(char *)AVI_header,HEADERBYTES);
if (i != HEADERBYTES)
{
close(AVI->fdes);
AVI_errno = AVI_ERR_WRITE;
free(AVI);
return 0;
}
AVI->pos = HEADERBYTES;
AVI->mode = AVI_MODE_WRITE; /* open for writing */
//init
AVI->anum = 0;
AVI->aptr = 0;
return AVI;
}
void AVI_set_video(avi_t *AVI, int width, int height, double fps, char *compressor)
{
/* may only be called if file is open for writing */
if(AVI->mode==AVI_MODE_READ) return;
AVI->width = width;
AVI->height = height;
AVI->fps = fps;