📄 lav_io.c
字号:
/* * Some routines for handling I/O from/to different video * file formats (currently AVI, Quicktime) * * These routines are isolated here in an extra file * in order to be able to handle more formats in the future. * * Copyright (C) 2000 Rainer Johanni <Rainer@Johanni.de> * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */#include <config.h>#include <errno.h>#include <string.h>#include <stdlib.h>#include <stdio.h>#include <fcntl.h>#define COMPILE_LAV_IO_C#include "lav_io.h"#ifdef HAVE_LIBDV#include <libdv/dv.h>#endif#ifdef HAVE_LIBQUICKTIME#include <quicktime.h>#include <lqt.h>#endifextern int AVI_errno;static char video_format=' ';static int internal_error=0;#define ERROR_JPEG 1#define ERROR_MALLOC 2#define ERROR_FORMAT 3#define ERROR_NOAUDIO 4static unsigned long jpeg_field_size = 0;static unsigned long jpeg_quant_offset = 0;static unsigned long jpeg_huffman_offset = 0;static unsigned long jpeg_image_offset = 0;static unsigned long jpeg_scan_offset = 0;static unsigned long jpeg_data_offset = 0;static unsigned long jpeg_padded_len = 0;static unsigned long jpeg_app0_offset = 0;static unsigned long jpeg_app1_offset = 0;#define M_SOF0 0xC0#define M_SOF1 0xC1#define M_DHT 0xC4#define M_SOI 0xD8 /* Start Of Image (beginning of datastream) */#define M_EOI 0xD9 /* End Of Image (end of datastream) */#define M_SOS 0xDA /* Start Of Scan (begins compressed data) */#define M_DQT 0xDB#define M_APP0 0xE0#define M_APP1 0xE1#define QUICKTIME_MJPG_TAG 0x6d6a7067 /* 'mjpg' */static int check_YUV420_input(lav_file_t *lav_fd);#ifdef HAVE_LIBDVstatic int check_DV2_input(lav_file_t *lav_fd);#endif#define TMP_EXTENSION ".tmp"#ifdef HAVE_LIBQUICKTIME/* put_int4: Put a 4 byte integer value into a character array as big endian number*/static void put_int4(unsigned char *buf, int val){ buf[0] = (val >> 24); buf[1] = (val >> 16); buf[2] = (val >> 8 ); buf[3] = (val );}#endif/* get_int2: get a 2 byte integer value from a character array as big endian number */static int get_int2(unsigned char *buff){ return (buff[0]*256 + buff[1]);}/* scan_jpeg: Scan jpeg data for markers, needed for Quicktime MJPA format and partly for AVI files. Taken mostly from Adam Williams' quicktime library */static int scan_jpeg(unsigned char * jpegdata, long jpeglen, int header_only){ int marker, length; long p; jpeg_field_size = 0; jpeg_quant_offset = 0; jpeg_huffman_offset = 0; jpeg_image_offset = 0; jpeg_scan_offset = 0; jpeg_data_offset = 0; jpeg_padded_len = 0; jpeg_app0_offset = 0; jpeg_app1_offset = 0; /* The initial marker must be SOI */ if (jpegdata[0] != 0xFF || jpegdata[1] != M_SOI) return -1; /* p is the pointer within the jpeg data */ p = 2; /* scan through the jpeg data */ while(p<jpeglen) { /* get next marker */ /* Find 0xFF byte; skip any non-FFs */ while(jpegdata[p] != 0xFF) { p++; if(p>=jpeglen) return -1; } /* Get marker code byte, swallowing any duplicate FF bytes */ while(jpegdata[p] == 0xFF) { p++; if(p>=jpeglen) return -1; } marker = jpegdata[p++]; if(p<=jpeglen-2) length = get_int2(jpegdata+p); else length = 0; /* We found a marker - check it */ if(marker == M_EOI) { jpeg_field_size = p; break; } switch(marker) { case M_SOF0: case M_SOF1: jpeg_image_offset = p-2; break; case M_DQT: if(jpeg_quant_offset==0) jpeg_quant_offset = p-2; break; case M_DHT: if(jpeg_huffman_offset==0) jpeg_huffman_offset = p-2; break; case M_SOS: jpeg_scan_offset = p-2; jpeg_data_offset = p+length; if(header_only) return 0; /* we are done with the headers */ break; case M_APP0: if(jpeg_app0_offset==0) jpeg_app0_offset = p-2; break; case M_APP1: if(jpeg_app1_offset==0) jpeg_app1_offset = p-2; break; } /* The pseudo marker as well as the markers M_TEM (0x01) and M_RST0 ... M_RST7 (0xd0 ... 0xd7) have no paramters. M_SOI and M_EOI also have no parameters, but we should never come here in that case */ if(marker == 0 || marker == 1 || (marker >= 0xd0 && marker <= 0xd7)) continue; /* skip length bytes */ if(p+length<=jpeglen) p += length; else return -1; } /* We are through parsing the jpeg data, we should have seen M_EOI */ if(!jpeg_field_size) return -1; /* Check for trailing garbage until jpeglen is reached or a new M_SOI is seen */ while(p<jpeglen) { if(p<jpeglen-1 && jpegdata[p]==0xFF && jpegdata[p+1]==M_SOI) break; p++; } jpeg_padded_len = p; return 0;}/* The query routines about the format */int lav_query_APP_marker(char format){ /* AVI needs the APP0 marker, Quicktime APP1 */ switch(format) { case 'a': return 0; case 'A': return 0; case 'j': return 0; case 'q': return 1; case 'm': return 0; default: return 0; }}int lav_query_APP_length(char format){ /* AVI: APP0 14 bytes, Quicktime APP1: 40 */ switch(format) { case 'a': return 14; case 'A': return 14; case 'j': return 14; case 'q': return 40; case 'm': return 0; default: return 0; }}int lav_query_polarity(char format){ /* Quicktime needs TOP_FIRST, for AVI we have the choice */ switch(format) { case 'a': return LAV_INTER_TOP_FIRST; case 'A': return LAV_INTER_BOTTOM_FIRST; case 'j': return LAV_INTER_TOP_FIRST; case 'q': return LAV_INTER_TOP_FIRST; case 'm': return LAV_INTER_TOP_FIRST; default: return LAV_INTER_TOP_FIRST; }}lav_file_t *lav_open_output_file(char *filename, char format, int width, int height, int interlaced, double fps, int asize, int achans, long arate){ lav_file_t *lav_fd = (lav_file_t*) malloc(sizeof(lav_file_t)); char *extension, *tempfile; if (lav_fd == 0) { internal_error=ERROR_MALLOC; return 0; } /* Set lav_fd */ lav_fd->avi_fd = 0; lav_fd->qt_fd = 0; lav_fd->format = format; /* Sanity check: do not create a quicktime file that is named with .avi */ extension = rindex(filename, '.'); if (extension != NULL) { extension++; switch(format) { case 'a': case 'A': if (strcmp(extension, "avi") && strcmp(extension, "AVI")) { internal_error = ERROR_FORMAT; return 0; } break; case 'q': if (strcmp(extension, "qt") && strcmp(extension, "QT") && strcmp(extension, "mov") && strcmp(extension, "MOV") && strcmp(extension,"moov") && strcmp(extension,"MOOV")) { internal_error = ERROR_FORMAT; return 0; } break; case 'j': if (strcmp(extension, "jpg") && strcmp(extension, "jpg") && strcmp(extension,"jpeg") && strcmp(extension,"JPEG")) { internal_error = ERROR_FORMAT; return 0; } break; } } lav_fd->interlacing = interlaced ? lav_query_polarity(format) : LAV_NOT_INTERLACED; lav_fd->has_audio = (asize>0 && achans>0); lav_fd->bps = (asize*achans+7)/8; lav_fd->MJPG_chroma = CHROMAUNKNOWN; switch(format) { case 'a': case 'A': /* Open AVI output file */ lav_fd->avi_fd = AVI_open_output_file(filename); if(!lav_fd->avi_fd) { free(lav_fd); return 0; } AVI_set_video(lav_fd->avi_fd, width, height, fps, "MJPG"); if (asize) AVI_set_audio(lav_fd->avi_fd, achans, arate, asize, WAVE_FORMAT_PCM); return lav_fd; case 'j': /* Open JPEG output file */ tempfile = (char *)malloc(strlen(filename) + strlen(TMP_EXTENSION) + 1); if (tempfile == NULL) { internal_error=ERROR_MALLOC; return(0); } strcpy(tempfile, filename); strcat(tempfile, TMP_EXTENSION); lav_fd->jpeg_filename = strdup(filename); lav_fd->jpeg_fd = open(tempfile, O_CREAT | O_TRUNC | O_WRONLY, 0644); free(tempfile); return lav_fd; case 'q':#ifdef HAVE_LIBQUICKTIME /* open quicktime output file */ /* since the documentation says that the file should be empty, we try to remove it first */ remove(filename); lav_fd->qt_fd = quicktime_open(filename, 0, 1); if(!lav_fd->qt_fd) { free(lav_fd); return 0; } quicktime_set_video(lav_fd->qt_fd, 1, width, height, fps, (interlaced ? QUICKTIME_MJPA : QUICKTIME_JPEG)); if (asize) quicktime_set_audio(lav_fd->qt_fd, achans, arate, asize, QUICKTIME_TWOS); return lav_fd;#else internal_error = ERROR_FORMAT; return 0;#endif default: return 0; }}int lav_close(lav_file_t *lav_file) { int res; char *tempfile; video_format = lav_file->format; internal_error = 0; /* for error messages */ switch (lav_file->format) { case 'a': case 'A': res = AVI_close( lav_file->avi_fd ); break; case 'j': tempfile = (char *)malloc(strlen(lav_file->jpeg_filename) + strlen(TMP_EXTENSION) + 1); if (tempfile == NULL) { res = -1; break; } strcpy(tempfile, lav_file->jpeg_filename); strcat(tempfile, TMP_EXTENSION); res = close(lav_file->jpeg_fd); rename(tempfile, lav_file->jpeg_filename); free(tempfile); free(lav_file->jpeg_filename); break;#ifdef HAVE_LIBQUICKTIME case 'q': res = quicktime_close( lav_file->qt_fd ); break;#endif default: res = -1; } free(lav_file); return res; }int lav_write_frame(lav_file_t *lav_file, uint8_t *buff, long size, long count){ int res, n; uint8_t *jpgdata = NULL; long jpglen = 0; video_format = lav_file->format; internal_error = 0; /* for error messages */ /* For interlaced video insert the apropriate APPn markers */ if(lav_file->interlacing!=LAV_NOT_INTERLACED) { switch(lav_file->format) { case 'a': case 'A': jpgdata = buff; jpglen = size; /* Loop over both fields */ for(n=0;n<2;n++) { /* For first field scan entire field, for second field scan the JPEG header, put in AVI1 + polarity. Be generous on errors */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -