AVILib Android AVI录像文件封装

来源:互联网 发布:日本传统乐器知乎 编辑:程序博客网 时间:2024/06/05 00:44

首先NDK编译相关的东西就不多说了,这里只介绍C代码的实现。

avilib.c

#include <stdio.h>#include <fcntl.h>#include <unistd.h>#include <stdlib.h>#include <string.h>#include <errno.h>#include "avilib.h"#include <android/log.h>#define LOG_TAG "AVIUtil"#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)avi_t* avi_handle;/* The following variable indicates the kind of error */long AVI_errno = 0;/******************************************************************* *                                                                 * *    Utilities for writing an AVI File                            * *                                                                 * *******************************************************************//* 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 2000000000/* HEADERBYTES: The number of bytes to reserve for the header */#define HEADERBYTES 2048#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 s;    s = ((AVI->a_bits + 7) / 8) * AVI->a_chans;    if (s == 0)        s = 1; /* 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 (write(AVI->fdes, c, 8) != 8            || write(AVI->fdes, data, length) != length) {        lseek(AVI->fdes, AVI->pos, SEEK_SET);        AVI_errno = AVI_ERR_WRITE;        return -1;    }    /* Update file position */    AVI->pos += 8 + length;    return 0;}static int avi_add_index_entry(avi_t *AVI, unsigned char *tag, long flags,        long pos, 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 */    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++;    return 0;}/* 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;    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 */    AVI->fdes = open(filename, O_RDWR | O_CREAT, 0600);    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 = write(AVI->fdes, 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 */    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;    memcpy(AVI->compressor, compressor, 4);    AVI->compressor[4] = 0;}void AVI_set_audio(avi_t *AVI, int channels, long rate, int bits, int format) {    /* may only be called if file is open for writing */    if (AVI->mode == AVI_MODE_READ)        return;    AVI->a_chans = channels;    AVI->a_rate = rate;    AVI->a_bits = bits;    AVI->a_fmt = format;}#define OUT4CC(s) \   if(nhb<=HEADERBYTES-4) memcpy(AVI_header+nhb,s,4); nhb += 4#define OUTLONG(n) \   if(nhb<=HEADERBYTES-4) long2str(AVI_header+nhb,n); nhb += 4#define OUTSHRT(n) \   if(nhb<=HEADERBYTES-2) { \      AVI_header[nhb  ] = (n   )&0xff; \      AVI_header[nhb+1] = (n>>8)&0xff; \   } \   nhb += 2/* Write the header of an AVI file and close it. returns 0 on success, -1 on write error. */static int avi_close_output_file(avi_t *AVI) {    int ret, njunk, sampsize, hasIndex, ms_per_frame, idxerror, flag;    int movi_len, hdrl_start, strl_start;    unsigned char AVI_header[HEADERBYTES];    long nhb;    /* Calculate length of movi list */    movi_len = AVI->pos - HEADERBYTES + 4;    /* Try to ouput the index entries. This may fail e.g. if no space     is left on device. We will report this as an error, but we still     try to write the header correctly (so that the file still may be     readable in the most cases */    idxerror = 0;    ret = avi_add_chunk(AVI, "idx1", (void*) AVI->idx, AVI->n_idx * 16);    hasIndex = (ret == 0);    if (ret) {        idxerror = 1;        AVI_errno = AVI_ERR_WRITE_INDEX;    }    /* Calculate Microseconds per frame */    if (AVI->fps < 0.001)        ms_per_frame = 0;    else        ms_per_frame = 1000000. / AVI->fps + 0.5;    /* Prepare the file header */    nhb = 0;    /* The RIFF header */    OUT4CC("RIFF");    OUTLONG(AVI->pos - 8);    /* # of bytes to follow */    OUT4CC("AVI ");    /* Start the header list */    OUT4CC("LIST");    OUTLONG(0);    /* Length of list in bytes, don't know yet */    hdrl_start = nhb; /* Store start position */    OUT4CC("hdrl");    /* The main AVI header */    /* The Flags in AVI File header */#define AVIF_HASINDEX           0x00000010      /* Index at end of file */#define AVIF_MUSTUSEINDEX       0x00000020#define AVIF_ISINTERLEAVED      0x00000100#define AVIF_TRUSTCKTYPE        0x00000800      /* Use CKType to find key frames */#define AVIF_WASCAPTUREFILE     0x00010000#define AVIF_COPYRIGHTED        0x00020000    OUT4CC("avih");    OUTLONG(56);    /* # of bytes to follow */    OUTLONG(ms_per_frame);    /* Microseconds per frame */    OUTLONG(10000000);    /* MaxBytesPerSec, I hope this will never be used */    OUTLONG(0);    /* PaddingGranularity (whatever that might be) */    /* Other sources call it 'reserved' */    flag = AVIF_WASCAPTUREFILE;    if (hasIndex)        flag |= AVIF_HASINDEX;    if (hasIndex && AVI->must_use_index)        flag |= AVIF_MUSTUSEINDEX;    OUTLONG(flag);    /* Flags */    OUTLONG(AVI->video_frames);    /* TotalFrames */    OUTLONG(0);    /* InitialFrames */    if (AVI->audio_bytes) {        OUTLONG(2);    } /* Streams */    else {        OUTLONG(1);    } /* Streams */    OUTLONG(0);    /* SuggestedBufferSize */    OUTLONG(AVI->width);    /* Width */    OUTLONG(AVI->height);    /* Height */    /* MS calls the following 'reserved': */    OUTLONG(0);    /* TimeScale:  Unit used to measure time */    OUTLONG(0);    /* DataRate:   Data rate of playback     */    OUTLONG(0);    /* StartTime:  Starting time of AVI data */    OUTLONG(0);    /* DataLength: Size of AVI data chunk    */    /* Start the video stream list ---------------------------------- */    OUT4CC("LIST");    OUTLONG(0);    /* Length of list in bytes, don't know yet */    strl_start = nhb; /* Store start position */    OUT4CC("strl");    /* The video stream header */    OUT4CC("strh");    OUTLONG(64);    /* # of bytes to follow */    OUT4CC("vids");    /* Type */    OUT4CC(AVI->compressor);    /* Handler */    OUTLONG(0);    /* Flags */    OUTLONG(0);    /* Reserved, MS says: wPriority, wLanguage */    OUTLONG(0);    /* InitialFrames */    OUTLONG(ms_per_frame);    /* Scale */    OUTLONG(1000000);    /* Rate: Rate/Scale == samples/second */    OUTLONG(0);    /* Start */    OUTLONG(AVI->video_frames);    /* Length */    OUTLONG(0);    /* SuggestedBufferSize */    OUTLONG(-1);    /* Quality */    OUTLONG(0);    /* SampleSize */    OUTLONG(0);    /* Frame */    OUTLONG(0);    /* Frame */    OUTLONG(0);    /* Frame */    OUTLONG(0);    /* Frame */    /* The video stream format */    OUT4CC("strf");    OUTLONG(40);    /* # of bytes to follow */    OUTLONG(40);    /* Size */    OUTLONG(AVI->width);    /* Width */    OUTLONG(AVI->height);    /* Height */    OUTSHRT(1);    OUTSHRT(24);    /* Planes, Count */    OUT4CC(AVI->compressor);    /* Compression */    OUTLONG(AVI->width*AVI->height);    /* SizeImage (in bytes?) */    OUTLONG(0);    /* XPelsPerMeter */    OUTLONG(0);    /* YPelsPerMeter */    OUTLONG(0);    /* ClrUsed: Number of colors used */    OUTLONG(0);    /* ClrImportant: Number of colors important */    /* Finish stream list, i.e. put number of bytes in the list to proper pos */    long2str(AVI_header + strl_start - 4, nhb - strl_start);    if (AVI->a_chans && AVI->audio_bytes) {        sampsize = avi_sampsize(AVI);        /* Start the audio stream list ---------------------------------- */        OUT4CC("LIST");        OUTLONG(0);        /* Length of list in bytes, don't know yet */        strl_start = nhb; /* Store start position */        OUT4CC("strl");        /* The audio stream header */        OUT4CC("strh");        OUTLONG(64);        /* # of bytes to follow */        OUT4CC("auds");        OUT4CC("\0\0\0\0");        OUTLONG(0);        /* Flags */        OUTLONG(0);        /* Reserved, MS says: wPriority, wLanguage */        OUTLONG(0);        /* InitialFrames */        OUTLONG(sampsize);        /* Scale */        OUTLONG(sampsize*AVI->a_rate);        /* Rate: Rate/Scale == samples/second */        OUTLONG(0);        /* Start */        OUTLONG(AVI->audio_bytes/sampsize);        /* Length */        OUTLONG(0);        /* SuggestedBufferSize */        OUTLONG(-1);        /* Quality */        OUTLONG(sampsize);        /* SampleSize */        OUTLONG(0);        /* Frame */        OUTLONG(0);        /* Frame */        OUTLONG(0);        /* Frame */        OUTLONG(0);        /* Frame */        /* The audio stream format */        OUT4CC("strf");        OUTLONG(16);        /* # of bytes to follow */        OUTSHRT(AVI->a_fmt);        /* Format */        OUTSHRT(AVI->a_chans);        /* Number of channels */        OUTLONG(AVI->a_rate);        /* SamplesPerSec */        OUTLONG(AVI->a_rate);        /* AvgBytesPerSec */        OUTSHRT(0x01);        /* BlockAlign */        OUTSHRT(0x08);        /* BitsPerSample */        /* Finish stream list, i.e. put number of bytes in the list to proper pos */        long2str(AVI_header + strl_start - 4, nhb - strl_start);    }    /* Finish header list */    long2str(AVI_header + hdrl_start - 4, nhb - hdrl_start);    /* Calculate the needed amount of junk bytes, output junk */    njunk = HEADERBYTES - nhb - 8 - 12;    /* Safety first: if njunk <= 0, somebody has played with     HEADERBYTES without knowing what (s)he did.     This is a fatal error */    if (njunk <= 0) {        fprintf(stderr, "AVI_close_output_file: # of header bytes too small\n");        exit(1);    }    OUT4CC("JUNK");    OUTLONG(njunk);    memset(AVI_header + nhb, 0, njunk);    nhb += njunk;    /* Start the movi list */    OUT4CC("LIST");    OUTLONG(movi_len);    /* Length of list in bytes */    OUT4CC("movi");    /* Output the header, truncate the file to the number of bytes     actually written, report an error if someting goes wrong */    if (lseek(AVI->fdes, 0, SEEK_SET) < 0            || write(AVI->fdes, AVI_header, HEADERBYTES) != HEADERBYTES            || ftruncate(AVI->fdes, AVI->pos) < 0) {        AVI_errno = AVI_ERR_CLOSE;        return -1;    }    if (idxerror)        return -1;    return 0;}/* AVI_write_data: Add video or audio data to the file; Return values: 0    No error; -1    Error, AVI_errno is set appropriatly; */static int avi_write_data(avi_t *AVI, char *data, long length, int audio) {    int n;    /* Check for maximum file length */    if ((AVI->pos + 8 + length + 8 + (AVI->n_idx + 1) * 16) > AVI_MAX_LEN) {        AVI_errno = AVI_ERR_SIZELIM;        return -1;    }    /* Add index entry */    if (audio)        n = avi_add_index_entry(AVI, "01wb", 0x00, AVI->pos, length);    else        n = avi_add_index_entry(AVI, "00db", 0x10, AVI->pos, length);    if (n)        return -1;    /* Output tag and data */    if (audio)        n = avi_add_chunk(AVI, "01wb", data, length);    else        n = avi_add_chunk(AVI, "00db", data, length);    if (n)        return -1;    return 0;}int AVI_write_frame(avi_t *AVI, char *data, long bytes) {    long pos;    if (AVI->mode == AVI_MODE_READ) {        AVI_errno = AVI_ERR_NOT_PERM;        return -1;    }    pos = AVI->pos;    if (avi_write_data(AVI, data, bytes, 0))        return -1;    AVI->last_pos = pos;    AVI->last_len = bytes;    AVI->video_frames++;    return 0;}int AVI_dup_frame(avi_t *AVI) {    if (AVI->mode == AVI_MODE_READ) {        AVI_errno = AVI_ERR_NOT_PERM;        return -1;    }    if (AVI->last_pos == 0)        return 0; /* No previous real frame */    if (avi_add_index_entry(AVI, "00db", 0x10, AVI->last_pos, AVI->last_len))        return -1;    AVI->video_frames++;    AVI->must_use_index = 1;    return 0;}int AVI_write_audio(avi_t *AVI, char *data, long bytes) {    if (AVI->mode == AVI_MODE_READ) {        AVI_errno = AVI_ERR_NOT_PERM;        return -1;    }    if (avi_write_data(AVI, data, bytes, 1))        return -1;    AVI->audio_bytes += bytes;    return 0;}long AVI_bytes_remain(avi_t *AVI) {    if (AVI->mode == AVI_MODE_READ)        return 0;    return (AVI_MAX_LEN - (AVI->pos + 8 + 16 * AVI->n_idx));}/******************************************************************* *                                                                 * *    Utilities for reading video and audio from an AVI File       * *                                                                 * *******************************************************************/int AVI_close(avi_t *AVI) {    int ret;    /* If the file was open for writing, the header and index still have     to be written */    if (AVI->mode == AVI_MODE_WRITE)        ret = avi_close_output_file(AVI);    else        ret = 0;    /* Even if there happened a error, we first clean up */    close(AVI->fdes);    if (AVI->idx)        free(AVI->idx);    if (AVI->video_index)        free(AVI->video_index);    if (AVI->audio_index)        free(AVI->audio_index);    free(AVI);    return ret;}#define ERR_EXIT(x) \{ \   AVI_close(AVI); \   AVI_errno = x; \   return 0; \}avi_t *AVI_open_input_file(char *filename, int getIndex) {    avi_t *AVI;    long i, n, rate, scale, idx_type;    unsigned char *hdrl_data;    long hdrl_len;    long nvi, nai, ioff;    long tot;    int lasttag = 0;    int vids_strh_seen = 0;    int vids_strf_seen = 0;    int auds_strh_seen = 0;    int auds_strf_seen = 0;    int num_stream = 0;    char data[256];    /* Create avi_t structure */    AVI = (avi_t *) malloc(sizeof(avi_t));    if (AVI == NULL) {        AVI_errno = AVI_ERR_NO_MEM;        return 0;    }    memset((void *) AVI, 0, sizeof(avi_t));    AVI->mode = AVI_MODE_READ; /* open for reading */    /* Open the file */    AVI->fdes = open(filename, O_RDONLY);    if (AVI->fdes < 0) {        AVI_errno = AVI_ERR_OPEN;        free(AVI);        return 0;    }    /* Read first 12 bytes and check that this is an AVI file */    if (read(AVI->fdes, data, 12) != 12)        ERR_EXIT(AVI_ERR_READ)    if (strncasecmp(data, "RIFF", 4) != 0            || strncasecmp(data + 8, "AVI ", 4) != 0)        ERR_EXIT(AVI_ERR_NO_AVI)    /* Go through the AVI file and extract the header list,     the start position of the 'movi' list and an optionally     present idx1 tag */    hdrl_data = 0;    while (1) {        if (read(AVI->fdes, data, 8) != 8)            break; /* We assume it's EOF */        n = str2ulong(data + 4);        n = PAD_EVEN(n);        if (strncasecmp(data, "LIST", 4) == 0) {            if (read(AVI->fdes, data, 4) != 4)                ERR_EXIT(AVI_ERR_READ)            n -= 4;            if (strncasecmp(data, "hdrl", 4) == 0) {                hdrl_len = n;                hdrl_data = (unsigned char *) malloc(n);                if (hdrl_data == 0)                    ERR_EXIT(AVI_ERR_NO_MEM)                if (read(AVI->fdes, hdrl_data, n) != n)                    ERR_EXIT(AVI_ERR_READ)            } else if (strncasecmp(data, "movi", 4) == 0) {                AVI->movi_start = lseek(AVI->fdes, 0, SEEK_CUR);                lseek(AVI->fdes, n, SEEK_CUR);            } else                lseek(AVI->fdes, n, SEEK_CUR);        } else if (strncasecmp(data, "idx1", 4) == 0) {            /* n must be a multiple of 16, but the reading does not             break if this is not the case */            AVI->n_idx = AVI->max_idx = n / 16;            AVI->idx = (unsigned char ((*)[16])) malloc(n);            if (AVI->idx == 0)                ERR_EXIT(AVI_ERR_NO_MEM)            if (read(AVI->fdes, AVI->idx, n) != n)                ERR_EXIT(AVI_ERR_READ)        } else            lseek(AVI->fdes, n, SEEK_CUR);    }    if (!hdrl_data)        ERR_EXIT(AVI_ERR_NO_HDRL)    if (!AVI->movi_start)        ERR_EXIT(AVI_ERR_NO_MOVI)    /* Interpret the header list */    for (i = 0; i < hdrl_len;) {        /* List tags are completly ignored */        if (strncasecmp(hdrl_data + i, "LIST", 4) == 0) {            i += 12;            continue;        }        n = str2ulong(hdrl_data + i + 4);        n = PAD_EVEN(n);        /* Interpret the tag and its args */        if (strncasecmp(hdrl_data + i, "strh", 4) == 0) {            i += 8;            if (strncasecmp(hdrl_data + i, "vids", 4) == 0 && !vids_strh_seen) {                memcpy(AVI->compressor, hdrl_data + i + 4, 4);                AVI->compressor[4] = 0;                scale = str2ulong(hdrl_data + i + 20);                rate = str2ulong(hdrl_data + i + 24);                if (scale != 0)                    AVI->fps = (double) rate / (double) scale;                AVI->video_frames = str2ulong(hdrl_data + i + 32);                AVI->video_strn = num_stream;                vids_strh_seen = 1;                lasttag = 1; /* vids */            } else if (strncasecmp(hdrl_data + i, "auds", 4) == 0                    && !auds_strh_seen) {                AVI->audio_bytes = str2ulong(hdrl_data + i + 32)                        * avi_sampsize(AVI);                AVI->audio_strn = num_stream;                auds_strh_seen = 1;                lasttag = 2; /* auds */            } else                lasttag = 0;            num_stream++;        } else if (strncasecmp(hdrl_data + i, "strf", 4) == 0) {            i += 8;            if (lasttag == 1) {                AVI->width = str2ulong(hdrl_data + i + 4);                AVI->height = str2ulong(hdrl_data + i + 8);                vids_strf_seen = 1;            } else if (lasttag == 2) {                AVI->a_fmt = str2ushort(hdrl_data + i);                AVI->a_chans = str2ushort(hdrl_data + i + 2);                AVI->a_rate = str2ulong(hdrl_data + i + 4);                AVI->a_bits = str2ushort(hdrl_data + i + 14);                auds_strf_seen = 1;            }            lasttag = 0;        } else {            i += 8;            lasttag = 0;        }        i += n;    }    free(hdrl_data);    if (!vids_strh_seen || !vids_strf_seen || AVI->video_frames == 0)        ERR_EXIT(AVI_ERR_NO_VIDS)    AVI->video_tag[0] = AVI->video_strn / 10 + '0';    AVI->video_tag[1] = AVI->video_strn % 10 + '0';    AVI->video_tag[2] = 'd';    AVI->video_tag[3] = 'b';    /* Audio tag is set to "99wb" if no audio present */    if (!AVI->a_chans)        AVI->audio_strn = 99;    AVI->audio_tag[0] = AVI->audio_strn / 10 + '0';    AVI->audio_tag[1] = AVI->audio_strn % 10 + '0';    AVI->audio_tag[2] = 'w';    AVI->audio_tag[3] = 'b';    lseek(AVI->fdes, AVI->movi_start, SEEK_SET);    /* get index if wanted */    if (!getIndex)        return AVI;    /* if the file has an idx1, check if this is relative     to the start of the file or to the start of the movi list */    idx_type = 0;    if (AVI->idx) {        long pos, len;        /* Search the first videoframe in the idx1 and look where         it is in the file */        for (i = 0; i < AVI->n_idx; i++)            if (strncasecmp(AVI->idx[i], AVI->video_tag, 3) == 0)                break;        if (i >= AVI->n_idx)            ERR_EXIT(AVI_ERR_NO_VIDS)        pos = str2ulong(AVI->idx[i] + 8);        len = str2ulong(AVI->idx[i] + 12);        lseek(AVI->fdes, pos, SEEK_SET);        if (read(AVI->fdes, data, 8) != 8)            ERR_EXIT(AVI_ERR_READ)        if (strncasecmp(data, AVI->idx[i], 4) == 0                && str2ulong(data + 4) == len) {            idx_type = 1; /* Index from start of file */        } else {            lseek(AVI->fdes, pos + AVI->movi_start - 4, SEEK_SET);            if (read(AVI->fdes, data, 8) != 8)                ERR_EXIT(AVI_ERR_READ)            if (strncasecmp(data, AVI->idx[i], 4) == 0                    && str2ulong(data + 4) == len) {                idx_type = 2; /* Index from start of movi list */            }        }        /* idx_type remains 0 if neither of the two tests above succeeds */    }    if (idx_type == 0) {        /* we must search through the file to get the index */        lseek(AVI->fdes, AVI->movi_start, SEEK_SET);        AVI->n_idx = 0;        while (1) {            if (read(AVI->fdes, data, 8) != 8)                break;            n = str2ulong(data + 4);            /* The movi list may contain sub-lists, ignore them */            if (strncasecmp(data, "LIST", 4) == 0) {                lseek(AVI->fdes, 4, SEEK_CUR);                continue;            }            /* Check if we got a tag ##db, ##dc or ##wb */            if (((data[2] == 'd' || data[2] == 'D')                    && (data[3] == 'b' || data[3] == 'B' || data[3] == 'c'                            || data[3] == 'C'))                    || ((data[2] == 'w' || data[2] == 'W')                            && (data[3] == 'b' || data[3] == 'B'))) {                avi_add_index_entry(AVI, data, 0,                        lseek(AVI->fdes, 0, SEEK_CUR) - 8, n);            }            lseek(AVI->fdes, PAD_EVEN(n), SEEK_CUR);        }        idx_type = 1;    }    /* Now generate the video index and audio index arrays */    nvi = 0;    nai = 0;    for (i = 0; i < AVI->n_idx; i++) {        if (strncasecmp(AVI->idx[i], AVI->video_tag, 3) == 0)            nvi++;        if (strncasecmp(AVI->idx[i], AVI->audio_tag, 4) == 0)            nai++;    }    AVI->video_frames = nvi;    AVI->audio_chunks = nai;    if (AVI->video_frames == 0)        ERR_EXIT(AVI_ERR_NO_VIDS)    AVI->video_index = (video_index_entry *) malloc(            nvi * sizeof(video_index_entry));    if (AVI->video_index == 0)        ERR_EXIT(AVI_ERR_NO_MEM)    if (AVI->audio_chunks) {        AVI->audio_index = (audio_index_entry *) malloc(                nai * sizeof(audio_index_entry));        if (AVI->audio_index == 0)            ERR_EXIT(AVI_ERR_NO_MEM)    }    nvi = 0;    nai = 0;    tot = 0;    ioff = idx_type == 1 ? 8 : AVI->movi_start + 4;    for (i = 0; i < AVI->n_idx; i++) {        if (strncasecmp(AVI->idx[i], AVI->video_tag, 3) == 0) {            AVI->video_index[nvi].pos = str2ulong(AVI->idx[i] + 8) + ioff;            AVI->video_index[nvi].len = str2ulong(AVI->idx[i] + 12);            nvi++;        }        if (strncasecmp(AVI->idx[i], AVI->audio_tag, 4) == 0) {            AVI->audio_index[nai].pos = str2ulong(AVI->idx[i] + 8) + ioff;            AVI->audio_index[nai].len = str2ulong(AVI->idx[i] + 12);            AVI->audio_index[nai].tot = tot;            tot += AVI->audio_index[nai].len;            nai++;        }    }    AVI->audio_bytes = tot;    /* Reposition the file */    lseek(AVI->fdes, AVI->movi_start, SEEK_SET);    AVI->video_pos = 0;    return AVI;}long AVI_video_frames(avi_t *AVI) {    return AVI->video_frames;}int AVI_video_width(avi_t *AVI) {    return AVI->width;}int AVI_video_height(avi_t *AVI) {    return AVI->height;}double AVI_frame_rate(avi_t *AVI) {    return AVI->fps;}char* AVI_video_compressor(avi_t *AVI) {    return AVI->compressor;}int AVI_audio_channels(avi_t *AVI) {    return AVI->a_chans;}int AVI_audio_bits(avi_t *AVI) {    return AVI->a_bits;}int AVI_audio_format(avi_t *AVI) {    return AVI->a_fmt;}long AVI_audio_rate(avi_t *AVI) {    return AVI->a_rate;}long AVI_audio_bytes(avi_t *AVI) {    return AVI->audio_bytes;}long AVI_frame_size(avi_t *AVI, long frame) {    if (AVI->mode == AVI_MODE_WRITE) {        AVI_errno = AVI_ERR_NOT_PERM;        return -1;    }    if (!AVI->video_index) {        AVI_errno = AVI_ERR_NO_IDX;        return -1;    }    if (frame < 0 || frame >= AVI->video_frames)        return 0;    return (AVI->video_index[frame].len);}long AVI_chunk_size(avi_t *AVI, long frame) {    if (AVI->mode == AVI_MODE_WRITE) {        AVI_errno = AVI_ERR_NOT_PERM;        return -1;    }    if (!AVI->audio_index) {        AVI_errno = AVI_ERR_NO_IDX;        return -1;    }    if (frame < 0 || frame >= AVI->video_frames)        return 0;    return (AVI->audio_index[frame].len);}int AVI_seek_start(avi_t *AVI) {    if (AVI->mode == AVI_MODE_WRITE) {        AVI_errno = AVI_ERR_NOT_PERM;        return -1;    }    lseek(AVI->fdes, AVI->movi_start, SEEK_SET);    AVI->video_pos = 0;    return 0;}int AVI_set_video_position(avi_t *AVI, long frame) {    if (AVI->mode == AVI_MODE_WRITE) {        AVI_errno = AVI_ERR_NOT_PERM;        return -1;    }    if (!AVI->video_index) {        AVI_errno = AVI_ERR_NO_IDX;        return -1;    }    if (frame < 0)        frame = 0;    AVI->video_pos = frame;    return 0;}long AVI_read_frame(avi_t *AVI, char *vidbuf) {    long n;    if (AVI->mode == AVI_MODE_WRITE) {        AVI_errno = AVI_ERR_NOT_PERM;        return -1;    }    if (!AVI->video_index) {        AVI_errno = AVI_ERR_NO_IDX;        return -1;    }    if (AVI->video_pos < 0 || AVI->video_pos >= AVI->video_frames)        return 0;    n = AVI->video_index[AVI->video_pos].len;    lseek(AVI->fdes, AVI->video_index[AVI->video_pos].pos, SEEK_SET);    if (read(AVI->fdes, vidbuf, n) != n) {        AVI_errno = AVI_ERR_READ;        return -1;    }    AVI->video_pos++;    return n;}int AVI_set_audio_position(avi_t *AVI, long byte) {    long n0, n1, n;    if (AVI->mode == AVI_MODE_WRITE) {        AVI_errno = AVI_ERR_NOT_PERM;        return -1;    }    if (!AVI->audio_index) {        AVI_errno = AVI_ERR_NO_IDX;        return -1;    }    if (byte < 0)        byte = 0;    /* Binary search in the audio chunks */    n0 = 0;    n1 = AVI->audio_chunks;    while (n0 < n1 - 1) {        n = (n0 + n1) / 2;        if (AVI->audio_index[n].tot > byte)            n1 = n;        else            n0 = n;    }    AVI->audio_posc = n0;    AVI->audio_posb = byte - AVI->audio_index[n0].tot;    return 0;}long AVI_read_audio(avi_t *AVI, char *audbuf, long bytes) {    long nr, pos, left, todo;    if (AVI->mode == AVI_MODE_WRITE) {        AVI_errno = AVI_ERR_NOT_PERM;        return -1;    }    if (!AVI->audio_index) {        AVI_errno = AVI_ERR_NO_IDX;        return -1;    }    nr = 0; /* total number of bytes read */    while (bytes > 0) {        left = AVI->audio_index[AVI->audio_posc].len - AVI->audio_posb;        if (left == 0) {            if (AVI->audio_posc >= AVI->audio_chunks - 1)                return nr;            AVI->audio_posc++;            AVI->audio_posb = 0;            continue;        }        if (bytes < left)            todo = bytes;        else            todo = left;        pos = AVI->audio_index[AVI->audio_posc].pos + AVI->audio_posb;        lseek(AVI->fdes, pos, SEEK_SET);        if (read(AVI->fdes, audbuf + nr, todo) != todo) {            AVI_errno = AVI_ERR_READ;            return -1;        }        bytes -= todo;        nr += todo;        AVI->audio_posb += todo;    }    return nr;}/* AVI_read_data: Special routine for reading the next audio or video chunk without having an index of the file. */int AVI_read_data(avi_t *AVI, char *vidbuf, long max_vidbuf, char *audbuf,        long max_audbuf, long *len) {    /*     * Return codes:     *     *    1 = video data read     *    2 = audio data read     *    0 = reached EOF     *   -1 = video buffer too small     *   -2 = audio buffer too small     */    int n;    char data[8];    if (AVI->mode == AVI_MODE_WRITE)        return 0;    while (1) {        /* Read tag and length */        if (read(AVI->fdes, data, 8) != 8)            return 0;        /* if we got a list tag, ignore it */        if (strncasecmp(data, "LIST", 4) == 0) {            lseek(AVI->fdes, 4, SEEK_CUR);            continue;        }        n = PAD_EVEN(str2ulong(data+4));        if (strncasecmp(data, AVI->video_tag, 3) == 0) {            *len = n;            AVI->video_pos++;            if (n > max_vidbuf) {                lseek(AVI->fdes, n, SEEK_CUR);                return -1;            }            if (read(AVI->fdes, vidbuf, n) != n)                return 0;            return 1;        } else if (strncasecmp(data, AVI->audio_tag, 4) == 0) {            *len = n;            if (n > max_audbuf) {                lseek(AVI->fdes, n, SEEK_CUR);                return -2;            }            if (read(AVI->fdes, audbuf, n) != n)                return 0;            return 2;            break;        } else if (lseek(AVI->fdes, n, SEEK_CUR) < 0)            return 0;    }}/* AVI_print_error: Print most recent error (similar to perror) */char *(avi_errors[]) = {/*  0 */"avilib - No Error",/*  1 */"avilib - AVI file size limit reached",/*  2 */"avilib - Error opening AVI file",/*  3 */"avilib - Error reading from AVI file",/*  4 */"avilib - Error writing to AVI file",/*  5 */"avilib - Error writing index (file may still be useable)",/*  6 */"avilib - Error closing AVI file",/*  7 */"avilib - Operation (read/write) not permitted",/*  8 */"avilib - Out of memory (malloc failed)",/*  9 */"avilib - Not an AVI file",/* 10 */"avilib - AVI file has no header list (corrupted?)",/* 11 */"avilib - AVI file has no MOVI list (corrupted?)",/* 12 */"avilib - AVI file has no video data",/* 13 */"avilib - operation needs an index",/* 14 */"avilib - Unkown Error" };static int num_avi_errors = sizeof(avi_errors) / sizeof(char*);static char error_string[4096];void AVI_print_error(char *str) {    int aerrno;    aerrno =            (AVI_errno >= 0 && AVI_errno < num_avi_errors) ?                    AVI_errno : num_avi_errors - 1;    fprintf(stderr, "%s: %s\n", str, avi_errors[aerrno]);    /* for the following errors, perror should report a more detailed reason: */    if (AVI_errno == AVI_ERR_OPEN || AVI_errno == AVI_ERR_READ            || AVI_errno == AVI_ERR_WRITE || AVI_errno == AVI_ERR_WRITE_INDEX            || AVI_errno == AVI_ERR_CLOSE) {        perror("REASON");    }}char *AVI_strerror() {    int aerrno;    aerrno =            (AVI_errno >= 0 && AVI_errno < num_avi_errors) ?                    AVI_errno : num_avi_errors - 1;    if (AVI_errno == AVI_ERR_OPEN || AVI_errno == AVI_ERR_READ            || AVI_errno == AVI_ERR_WRITE || AVI_errno == AVI_ERR_WRITE_INDEX            || AVI_errno == AVI_ERR_CLOSE) {        sprintf(error_string, "%s - %s", avi_errors[aerrno], strerror(errno));        return error_string;    } else {        return avi_errors[aerrno];    }}/* * Class:     com_android_concox_AVIUtil * Method:    init * Signature: (Ljava/lang/String;)I */JNIEXPORT jint JNICALL Java_com_android_AVIUtil_init(JNIEnv * env,        jclass obj, jstring path, jint width, jint height, jint fps) {    const char* filename = (*env)->GetStringUTFChars(env, path, NULL);    LOGD("filename:", filename);    avi_handle = AVI_open_output_file((char*) filename);    LOGD("%d\n", __LINE__);    if (!avi_handle) {        LOGD("%d\n", __LINE__);        return -1;    }    AVI_set_video(avi_handle, 854, 480, 15, "H264");    LOGD("%d\n", __LINE__);    AVI_set_audio(avi_handle, 1, 8000, 16, WAVE_FORMAT_ALAW);    LOGD("%d\n", __LINE__);    return 0;}/* * Class:     com_android_concox_AVIUtil * Method:    writeVideo * Signature: ([BI)I */unsigned int count = 0;JNIEXPORT jint JNICALL Java_com_android_AVIUtil_writeVideo(JNIEnv * env,        jclass obj, jbyteArray video, jint len, jlong videoTime) {    jbyte * videoBuf = (jbyte*) (*env)->GetByteArrayElements(env, video, 0);    static int frame_num = 0;    static unsigned int prev_video_time = 0;    AVI_set_video_position(avi_handle, frame_num++);    if (count % 15 == 0) {        if (count != 0) {            prev_video_time += 10;        }    }    if (prev_video_time > videoTime + 1000) {        videoTime += 4294967;    }    if (prev_video_time == 0) {        prev_video_time = videoTime;    } else {        while ((prev_video_time + 66) < videoTime) {            AVI_write_frame(avi_handle, NULL, 0);            prev_video_time += 66;            count++;        }        count++;        prev_video_time += 66;    }    int ret = -1;    ret = AVI_write_frame(avi_handle, (char *) videoBuf, (long) len);    if (ret < 0) {        (*env)->ReleaseByteArrayElements(env, video, videoBuf, 0);        return -1;    }    (*env)->ReleaseByteArrayElements(env, video, videoBuf, 0);    return 0;}/* * Class:     com_android_concox_AVIUtil * Method:    writeAudio * Signature: ([BI)I */JNIEXPORT jint JNICALL Java_com_android_AVIUtil_writeAudio(JNIEnv * env,        jclass obj, jbyteArray audio, jint len) {    jbyte * audioBuf = (jbyte*) (*env)->GetByteArrayElements(env, audio, 0);    static int frame_num = 0;    AVI_set_audio_position(avi_handle, frame_num++);    LOGD("%d\n", __LINE__);    if (AVI_write_audio(avi_handle, (char*) audioBuf, (long) len) != 0) {        LOGD("%d\n", __LINE__);        (*env)->ReleaseByteArrayElements(env, audio, audioBuf, 0);        return -1;    }    LOGD("%d\n", __LINE__);    (*env)->ReleaseByteArrayElements(env, audio, audioBuf, 0);    return 0;}/* * Class:     com_android_concox_AVIUtil * Method:    close * Signature: ()I */JNIEXPORT jint JNICALL Java_com_android_AVIUtil_close(JNIEnv * env,        jclass obj) {    AVI_close(avi_handle);}

avilib.h

#include <jni.h>#ifndef AVILIB_H#define AVILIB_Htypedef struct {    long pos;    long len;} video_index_entry;typedef struct {    long pos;    long len;    long tot;} audio_index_entry;typedef struct {    long fdes; /* File descriptor of AVI file */    long mode; /* 0 for reading, 1 for writing */    long width; /* Width  of a video frame */    long height; /* Height of a video frame */    double fps; /* Frames per second */    char compressor[8]; /* Type of compressor, 4 bytes + padding for 0 byte */    long video_strn; /* Video stream number */    long video_frames; /* Number of video frames */    char video_tag[4]; /* Tag of video data */    long video_pos; /* Number of next frame to be read     (if index present) */    long a_fmt; /* Audio format, see #defines below */    long a_chans; /* Audio channels, 0 for no audio */    long a_rate; /* Rate in Hz */    long a_bits; /* bits per audio sample */    long audio_strn; /* Audio stream number */    long audio_bytes; /* Total number of bytes of audio data */    long audio_chunks; /* Chunks of audio data in the file */    char audio_tag[4]; /* Tag of audio data */    long audio_posc; /* Audio position: chunk */    long audio_posb; /* Audio position: byte within chunk */    long pos; /* position in file */    long n_idx; /* number of index entries actually filled */    long max_idx; /* number of index entries actually allocated */    unsigned char (*idx)[16]; /* index entries (AVI idx1 tag) */    video_index_entry * video_index;    audio_index_entry * audio_index;    long last_pos; /* Position of last frame written */    long last_len; /* Length of last frame written */    int must_use_index; /* Flag if frames are duplicated */    long movi_start;} avi_t;#define AVI_MODE_WRITE  0#define AVI_MODE_READ   1/* The error codes delivered by avi_open_input_file */#define AVI_ERR_SIZELIM      1     /* The write of the data would exceed                                      the maximum size of the AVI file.                                      This is more a warning than an error                                      since the file may be closed safely */#define AVI_ERR_OPEN         2     /* Error opening the AVI file - wrong path                                      name or file nor readable/writable */#define AVI_ERR_READ         3     /* Error reading from AVI File */#define AVI_ERR_WRITE        4     /* Error writing to AVI File,                                      disk full ??? */#define AVI_ERR_WRITE_INDEX  5     /* Could not write index to AVI file                                      during close, file may still be                                      usable */#define AVI_ERR_CLOSE        6     /* Could not write header to AVI file                                      or not truncate the file during close,                                      file is most probably corrupted */#define AVI_ERR_NOT_PERM     7     /* Operation not permitted:                                      trying to read from a file open                                      for writing or vice versa */#define AVI_ERR_NO_MEM       8     /* malloc failed */#define AVI_ERR_NO_AVI       9     /* Not an AVI file */#define AVI_ERR_NO_HDRL     10     /* AVI file has no has no header list,                                      corrupted ??? */#define AVI_ERR_NO_MOVI     11     /* AVI file has no has no MOVI list,                                      corrupted ??? */#define AVI_ERR_NO_VIDS     12     /* AVI file contains no video data */#define AVI_ERR_NO_IDX      13     /* The file has been opened with                                      getIndex==0, but an operation has been                                      performed that needs an index *//* Possible Audio formats */#define WAVE_FORMAT_UNKNOWN             (0x0000)#define WAVE_FORMAT_PCM                 (0x0001)#define WAVE_FORMAT_ADPCM               (0x0002)#define WAVE_FORMAT_IBM_CVSD            (0x0005)#define WAVE_FORMAT_ALAW                (0x0006)#define WAVE_FORMAT_MULAW               (0x0007)#define WAVE_FORMAT_OKI_ADPCM           (0x0010)#define WAVE_FORMAT_DVI_ADPCM           (0x0011)#define WAVE_FORMAT_DIGISTD             (0x0015)#define WAVE_FORMAT_DIGIFIX             (0x0016)#define WAVE_FORMAT_YAMAHA_ADPCM        (0x0020)#define WAVE_FORMAT_DSP_TRUESPEECH      (0x0022)#define WAVE_FORMAT_GSM610              (0x0031)#define IBM_FORMAT_MULAW                (0x0101)#define IBM_FORMAT_ALAW                 (0x0102)#define IBM_FORMAT_ADPCM                (0x0103)avi_t* AVI_open_output_file(char * filename);void AVI_set_video(avi_t *AVI, int width, int height, double fps,        char *compressor);void AVI_set_audio(avi_t *AVI, int channels, long rate, int bits, int format);int AVI_write_frame(avi_t *AVI, char *data, long bytes);int AVI_dup_frame(avi_t *AVI);int AVI_write_audio(avi_t *AVI, char *data, long bytes);long AVI_bytes_remain(avi_t *AVI);int AVI_close(avi_t *AVI);avi_t *AVI_open_input_file(char *filename, int getIndex);long AVI_video_frames(avi_t *AVI);int AVI_video_width(avi_t *AVI);int AVI_video_height(avi_t *AVI);double AVI_frame_rate(avi_t *AVI);char* AVI_video_compressor(avi_t *AVI);int AVI_audio_channels(avi_t *AVI);int AVI_audio_bits(avi_t *AVI);int AVI_audio_format(avi_t *AVI);long AVI_audio_rate(avi_t *AVI);long AVI_audio_bytes(avi_t *AVI);long AVI_frame_size(avi_t *AVI, long frame);long AVI_chunk_size(avi_t *AVI, long frame);int AVI_seek_start(avi_t *AVI);int AVI_set_video_position(avi_t *AVI, long frame);long AVI_read_frame(avi_t *AVI, char *vidbuf);int AVI_set_audio_position(avi_t *AVI, long byte);long AVI_read_audio(avi_t *AVI, char *audbuf, long bytes);int AVI_read_data(avi_t *AVI, char *vidbuf, long max_vidbuf, char *audbuf,        long max_audbuf, long *len);void AVI_print_error(char *str);char *AVI_strerror();char *AVI_syserror();#endifJNIEXPORT jint JNICALL Java_com_android_AVIUtil_init(JNIEnv *, jclass,        jstring, jint, jint, jint);/* * Class:     com_android_concox_AVIUtil * Method:    writeVideo * Signature: ([BI)I */JNIEXPORT jint JNICALL Java_com_android_AVIUtil_writeVideo(JNIEnv *,        jclass, jbyteArray, jint, jlong);/* * Class:     com_android_AVIUtil * Method:    writeAudio * Signature: ([BI)I */JNIEXPORT jint JNICALL Java_com_android_AVIUtil_writeAudio(JNIEnv *,        jclass, jbyteArray, jint);/* * Class:     com_android_AVIUtil * Method:    close * Signature: ()I */JNIEXPORT jint JNICALL Java_com_android_AVIUtil_close(JNIEnv *, jclass);

如此就可以完成裸码流直接写文件的封装了,超轻量级,对比ffmpeg,这个库更小。

2 0
原创粉丝点击