/* * Some routines for handling I/O from/to different video * file formats (currently AVI, Quicktime and movtar). * * 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 * * 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 #include #include #include #include #include #include #include //#include #include #ifdef USE_GDK_PIXBUF #include #endif #include #ifdef HAVE_LIBQUICKTIME #include #include #include #endif #include #include #include #include #define QUICKTIME_MJPG_TAG 0x6d6a7067 extern int vj_el_get_decoder_from_fourcc( const char *fourcc ); extern int get_ffmpeg_pixfmt(int p); extern int AVI_errno; static int _lav_io_default_chroma = CHROMAUNKNOWN; static char video_format=' '; static int internal_error=0; #define ERROR_JPEG 1 #define ERROR_MALLOC 2 #define ERROR_FORMAT 3 #define ERROR_NOAUDIO 4 #ifdef USE_GDK_PIXBUF static int output_scale_width = 0; static int output_scale_height = 0; static float output_fps = 25.0; static int output_yuv = 1; // 422 void lav_set_project(int w, int h, float f, int fmt) { output_scale_width = w; output_scale_height = h; output_fps = f; output_yuv = fmt; } #else void lav_set_project(int w, int h, float f, int fmt) { } #endif #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 TMP_EXTENSION ".tmp" /* The query routines about the format */ int lav_query_APP_marker(char format) { /* AVI needs the APP0 marker, Quicktime APP1 */ switch(format) { case 'Q': case 'q': return 1; default: return 0; } } int lav_query_APP_length(char format) { /* AVI: APP0 14 bytes, Quicktime APP1: 40 */ switch(format) { case 'Q': case 'q': return 40; case 'm': return 0; default: return 14; } } int lav_query_polarity(char format) { /* Quicktime needs TOP_FIRST, for AVI we have the choice */ switch(format) { /* case 'b': // todo: must implement polarity for DV? return LAV_NOT_INTERLACED; case 'a': return LAV_INTER_TOP_FIRST; case 'A': return LAV_INTER_BOTTOM_FIRST; case 'D': return LAV_NOT_INTERLACED; //divx case 'Y': return LAV_NOT_INTERLACED; // planar yuv 4:2:0 (yv12) case 'P': return LAV_NOT_INTERLACED; // planar yuv 4:2:2 (yv16) case 'V': return LAV_NOT_INTERLACED; // planar yuv 4:2:0 (yv12) case 'v': return LAV_NOT_INTERLACED; // planar yuv 4:2:2 (yv16) case 'M': return LAV_NOT_INTERLACED; // mpeg4 , case 'd': return LAV_INTER_BOTTOM_FIRST; // DV, interlaced case 'j': return LAV_INTER_TOP_FIRST; case 'q': return LAV_INTER_TOP_FIRST; case 'L': return LAV_NOT_INTERLACED; case 'l': return LAV_NOT_INTERLACED; case 'm': return LAV_INTER_TOP_FIRST; case 'x': return LAV_NOT_INTERLACED; // picture is always not interlaced*/ default: return LAV_NOT_INTERLACED; } } void lav_set_default_chroma(int _chroma) { if(_chroma == CHROMAUNKNOWN || _chroma == CHROMA420 || _chroma == CHROMA422 || _chroma == CHROMA444 || _chroma == CHROMA422F || _chroma == CHROMA420F) _lav_io_default_chroma = _chroma; } 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*) vj_malloc(sizeof(lav_file_t)); if(lav_fd==0) { internal_error=ERROR_MALLOC; return NULL; } /* Set lav_fd */ lav_fd->avi_fd = 0; lav_fd->format = format; 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->is_MJPG = 1; lav_fd->MJPG_chroma = _lav_io_default_chroma; char fourcc[16]; int is_avi = 1; switch(format) { case 'a': case 'A': /* Open AVI output file */ veejay_msg(VEEJAY_MSG_DEBUG, "\tWriting output file in AVI MJPEG"); sprintf(fourcc, "MJPG" ); break; case 'c': veejay_msg(VEEJAY_MSG_DEBUG, "\tWriting output file in AVI MJPEG-b"); sprintf(fourcc, "MJPB" ); break; case 'l': veejay_msg(VEEJAY_MSG_DEBUG, "\tWriting output file in AVI LJPEG"); sprintf(fourcc, "JPGL"); break; case 'L': veejay_msg(VEEJAY_MSG_DEBUG, "\tWriting output file in AVI LZO (veejay's fourcc)"); sprintf(fourcc, "MLZO" ); break; case 'v': veejay_msg(VEEJAY_MSG_DEBUG, "\tWriting output file in AVI VJ20 (veejay's fourcc)"); sprintf(fourcc,"VJ20"); break; case 'V': veejay_msg(VEEJAY_MSG_DEBUG, "\tWriting output file in AVI VJ22 (veejay's fourcc)"); sprintf(fourcc,"VJ22"); break; case 'Y': veejay_msg(VEEJAY_MSG_DEBUG, "\tWriting output file in AVI IYUV"); sprintf(fourcc, "IYUV" ); break; case 'P': veejay_msg(VEEJAY_MSG_DEBUG, "\tWriting output file in AVI YV16"); sprintf(fourcc, "YV16"); break; case 'D': veejay_msg(VEEJAY_MSG_DEBUG, "\tWriting output file in AVI DIV3"); sprintf(fourcc, "DIV3"); break; case 'M': veejay_msg(VEEJAY_MSG_DEBUG, "\tWriting output file in AVI MP4V"); sprintf(fourcc,"MP4V"); break; case 'b': case 'd': veejay_msg(VEEJAY_MSG_DEBUG, "\tWriting output file in AVI DVSD"); sprintf(fourcc, "DVSD"); break; case 'q': case 'Q': veejay_msg(VEEJAY_MSG_DEBUG, "\tWriting output file in Quicktime MJPA/JPEG"); is_avi = 0; break; case 'x': is_avi = 0; break; } if( is_avi ) { lav_fd->avi_fd = AVI_open_output_file(filename); if(!lav_fd->avi_fd) { free(lav_fd); return NULL; } AVI_set_video(lav_fd->avi_fd, width, height, fps, fourcc ); if (asize) { if(AVI_set_audio(lav_fd->avi_fd, achans, arate, asize, WAVE_FORMAT_PCM)==-1) { veejay_msg(0, "Too many channels or invalid AVI file"); lav_close( lav_fd ); return NULL; } } return lav_fd; } else { #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) { veejay_msg(VEEJAY_MSG_ERROR, "\tCannot open '%s' for writing", filename); free(lav_fd); return NULL; } if(format=='q') quicktime_set_video(lav_fd->qt_fd, 1, width, height, fps, (interlaced ? QUICKTIME_MJPA : QUICKTIME_JPEG)); else quicktime_set_video(lav_fd->qt_fd,1, width,height,fps, QUICKTIME_DV ); if (asize) quicktime_set_audio(lav_fd->qt_fd, achans, arate, asize, QUICKTIME_TWOS); int has_kf = quicktime_has_keyframes( lav_fd->qt_fd, 0 ); char *copyright = quicktime_get_copyright( lav_fd->qt_fd ); char *name = quicktime_get_name( lav_fd->qt_fd ); char *info = quicktime_get_info( lav_fd->qt_fd ); veejay_msg(VEEJAY_MSG_DEBUG, "(C) %s by %s, %s, has keyframes = %d", copyright,name,info,has_kf ); return lav_fd; #else veejay_msg(0,"Quicktime not compiled in, cannot use Quicktime."); internal_error = ERROR_FORMAT; return NULL; #endif } if(lav_fd) free(lav_fd); return NULL; } int lav_close(lav_file_t *lav_file) { int ret = 0; video_format = lav_file->format; internal_error = 0; /* for error messages */ switch(video_format) { #ifdef SUPPORT_READ_DV2 case 'b': if( lav_file->dv_fd ) { ret = rawdv_close(lav_file->dv_fd); } break; #endif case 'x': vj_picture_cleanup( lav_file->picture ); ret = 1; break; #ifdef HAVE_LIBQUICKTIME case 'q': if( lav_file->qt_fd ) { ret = quicktime_close( lav_file->qt_fd ); } break; #endif default: if( lav_file->avi_fd ) { ret = AVI_close(lav_file->avi_fd); } break; } if(lav_file) free(lav_file); lav_file = NULL; return ret; } long lav_bytes_remain( lav_file_t *lav_file ) { switch( lav_file->format ) { case 'a': case 'A': case 'M': case 'P': case 'D': case 'v': case 'V': case 'Y': case 'L': case 'l': case 'd': return AVI_bytes_remain( lav_file->avi_fd ); default: return -1; } return -1; } int lav_write_frame(lav_file_t *lav_file, uint8_t *buff, long size, long count) { int res=0, n; video_format = lav_file->format; internal_error = 0; /* for error messages */ #ifdef SUPPORT_READ_DV2 if(video_format == 'b') return -1; //rawdv, no stream writing support yet #endif /* For interlaced video insert the apropriate APPn markers */ #ifdef USE_GDK_PIXBUF if(video_format == 'x') return -1;//picture #endif for(n=0;nformat) { case 'a': case 'A': case 'M': case 'P': case 'D': case 'v': case 'V': case 'Y': case 'L': case 'l': case 'd': if(n==0) { res = AVI_write_frame( lav_file->avi_fd, buff, size ); } else { res = AVI_dup_frame( lav_file->avi_fd ); } break; #ifdef HAVE_LIBQUICKTIME case 'q': case 'Q': res = quicktime_write_frame( lav_file->qt_fd, buff, size, 0 ); break; #endif default: res = -1; break; } } return res; } int lav_write_audio(lav_file_t *lav_file, uint8_t *buff, long samps) { #ifdef HAVE_LIBQUICKTIME int i, j; int16_t *qt_audio = (int16_t *)buff, **qt_audion; int channels = lav_audio_channels(lav_file); int bits = lav_audio_bits(lav_file); #ifdef HAVE_LIBQUICKTIME int res=0; #endif qt_audion = malloc(channels * sizeof (int16_t **)); for (i = 0; i < channels; i++) qt_audion[i] = (int16_t *)malloc(samps * lav_file->bps); #endif switch(lav_file->format ) { #ifdef HAVE_LIBQUICKTIME case 'q': case 'Q': if (bits != 16 || channels > 1) { /* Deinterleave the audio into the two channels and/or convert * bits per sample to the required format. */ qt_audion = malloc(channels * sizeof(*qt_audion)); for (i = 0; i < channels; i++) qt_audion[i] = malloc(samps * sizeof(**qt_audion)); if (bits == 16) for (i = 0; i < samps; i++) for (j = 0; j < channels; j++) qt_audion[j][i] = qt_audio[channels * i + j]; else if (bits == 8) for (i = 0; i < samps; i++) for (j = 0; j < channels; j++) qt_audion[j][i] = ((int16_t)(buff[channels * i + j]) << 8) ^ 0x8000; if (bits == 8 || bits == 16) res = lqt_encode_audio_track(lav_file->qt_fd, qt_audion, NULL, samps, 0); for (i = 0; i < channels; i++) free(qt_audion[i]); free(qt_audion); } else { qt_audion = &qt_audio; res = lqt_encode_audio_track(lav_file->qt_fd, qt_audion, NULL, samps, 0); } return res; break; #endif #ifdef SUPPORT_READ_DV2 case 'b': return 0; #endif #ifdef USE_GDK_PIXBUF case 'x': return 0; #endif default: return AVI_write_audio( lav_file->avi_fd, buff, samps*lav_file->bps); } return 0; } void lav_bogus_set_length( lav_file_t *lav_file , int len ) { lav_file->bogus_len = len; } int lav_bogus_video_length( lav_file_t *lav_file ) { video_format = lav_file->format; if( lav_file->format == 'x' ) return lav_file->bogus_len; return 0; } long lav_video_frames(lav_file_t *lav_file) { video_format = lav_file->format; internal_error = 0; /* for error messages */ switch(lav_file->format) { #ifdef SUPPORT_READ_DV2 case 'b': return rawdv_video_frames(lav_file->dv_fd); #endif #ifdef USE_GDK_PIXBUF case 'x': return lav_file->bogus_len; #endif #ifdef HAVE_LIBQUICKTIME case 'q': case 'Q': return quicktime_video_length(lav_file->qt_fd,0); #endif default: return AVI_video_frames( lav_file->avi_fd ); } return -1; } int lav_video_width(lav_file_t *lav_file) { video_format = lav_file->format; internal_error = 0; /* for error messages */ switch(lav_file->format) { #ifdef SUPPORT_READ_DV2 case 'b': return rawdv_width(lav_file->dv_fd); #endif #ifdef USE_GDK_PIXBUF case 'x': return output_scale_width; #endif #ifdef HAVE_LIBQUICKTIME case 'q': case 'Q': return quicktime_video_width(lav_file->qt_fd,0); #endif default: return AVI_video_width( lav_file->avi_fd); } return -1; } int lav_video_height(lav_file_t *lav_file) { video_format = lav_file->format; internal_error = 0; /* for error messages */ switch( lav_file->format ) { #ifdef SUPPORT_READ_DV2 case 'b': return rawdv_height( lav_file->dv_fd ); #endif #ifdef USE_GDK_PIXBUF case 'x': return output_scale_height; #endif #ifdef HAVE_LIBQUICKTIME case 'q': case 'Q': return quicktime_video_height(lav_file->qt_fd,0); #endif default: return AVI_video_height(lav_file->avi_fd); } return -1; } double lav_frame_rate(lav_file_t *lav_file) { video_format = lav_file->format; internal_error = 0; /* for error messages */ switch(lav_file->format) { #ifdef SUPPORT_READ_DV2 case 'b': return rawdv_fps(lav_file->dv_fd); #endif #ifdef USE_GDK_PIXBUF case 'x': return output_fps; #endif #ifdef HAVE_LIBQUICKTIME case 'q': case 'Q': return quicktime_frame_rate(lav_file->qt_fd,0); #endif default: return AVI_frame_rate( lav_file->avi_fd ); } return -1; } int lav_video_interlacing(lav_file_t *lav_file) { #ifdef SUPPORT_READ_DV2 if(video_format == 'b') return rawdv_interlacing(lav_file->dv_fd); #endif #ifdef USE_GDK_PIXBUF if(video_format == 'x') return LAV_NOT_INTERLACED; #endif return lav_file->interlacing; } void lav_video_clipaspect(lav_file_t *lav_file, int *sar_w, int *sar_h) { *sar_w = lav_file->sar_w; *sar_h = lav_file->sar_h; return; } int lav_video_is_MJPG(lav_file_t *lav_file) { return lav_file->is_MJPG; } int lav_video_MJPG_chroma(lav_file_t *lav_file) { return lav_file->MJPG_chroma; } int lav_is_yuv_planar( int pix_fmt ) { switch(pix_fmt){ case PIX_FMT_YUVJ420P: case PIX_FMT_YUVJ422P: case PIX_FMT_YUVJ444P: case PIX_FMT_YUV420P: case PIX_FMT_YUV422P: case PIX_FMT_YUV444P: return 1; } return 0; } int lav_video_cmodel( lav_file_t *lav_file) { switch(lav_file->MJPG_chroma) { case CHROMA411: return PIX_FMT_YUV411P; case CHROMA420: return PIX_FMT_YUV420P; case CHROMA422: return PIX_FMT_YUV422P; case CHROMA420F: return PIX_FMT_YUVJ420P; case CHROMA422F: return PIX_FMT_YUVJ422P; case CHROMA444: return PIX_FMT_YUV444P; default: return -1; } return -1; } int lav_video_is_qt( lav_file_t *lav_file) { #ifdef HAVE_LIBQUICK_TIME if( lav_file->qt_fd) return 1; #endif return 0; } int lav_video_compressor_type(lav_file_t *lav_file) { #ifdef SUPPORT_READ_DV2 if(lav_file->format == 'b') return rawdv_compressor( lav_file->dv_fd ); #endif #ifdef USE_GDK_PIXBUF if(lav_file->format == 'x') return 0xffff; #endif #ifdef HAVE_LIBQUICKTIME if(lav_file->format == 'q' || lav_file->format == 'Q') { const char *compressor = quicktime_video_compressor(lav_file->qt_fd,0); return vj_el_get_decoder_from_fourcc( compressor ); } #endif return vj_el_get_decoder_from_fourcc( AVI_video_compressor(lav_file->avi_fd) ); } #define FOURCC_DV "dvsd" #define FOURCC_PIC "pict" #define FOURCC_LZO "mlzo" const char *lav_video_compressor(lav_file_t *lav_file) { video_format = lav_file->format; internal_error = 0; /* for error messages */ #ifdef SUPPORT_READ_DV2 if( video_format == 'b' ) { return FOURCC_DV; } #endif #ifdef USE_GDK_PIXBUF if( video_format == 'x') { return FOURCC_PIC; } #endif if( video_format == 'L' ) { return FOURCC_LZO; } #ifdef HAVE_LIBQUICKTIME if(lav_file->format == 'q' || lav_file->format == 'Q') return quicktime_video_compressor(lav_file->qt_fd,0); #endif return AVI_video_compressor(lav_file->avi_fd); } int lav_audio_channels(lav_file_t *lav_file) { if(!lav_file->has_audio) return 0; video_format = lav_file->format; internal_error = 0; /* for error messages */ #ifdef SUPPORT_READ_DV2 if(video_format == 'b') return rawdv_audio_channels(lav_file->dv_fd); #endif #ifdef USE_GDK_PIXBUF if(video_format == 'x') return 0; #endif #ifdef HAVE_LIBQUICKTIME if(video_format == 'q' || video_format =='Q') return quicktime_track_channels(lav_file->qt_fd,0); #endif return AVI_audio_channels(lav_file->avi_fd); } int lav_audio_bits(lav_file_t *lav_file) { if(!lav_file->has_audio) return 0; video_format = lav_file->format; internal_error = 0; /* for error messages */ #ifdef SUPPORT_READ_DV2 if(video_format == 'b') return rawdv_audio_bits(lav_file->dv_fd); #endif #ifdef USE_GDK_PIXBUF if(video_format == 'x' ) return 0; #endif #ifdef HAVE_LIBQUICKTIME if(video_format == 'q'|| video_format =='Q') return quicktime_audio_bits(lav_file->qt_fd,0); #endif return (AVI_audio_bits(lav_file->avi_fd)); } long lav_audio_rate(lav_file_t *lav_file) { if(!lav_file->has_audio) return 0; video_format = lav_file->format; internal_error = 0; /* for error messages */ #ifdef SUPPORT_READ_DV2 if(video_format=='b') return rawdv_audio_rate(lav_file->dv_fd); #endif #ifdef USE_GDK_PIXBUF if(video_format == 'x') return 0; #endif #ifdef HAVE_LIBQUICKTIME if( video_format == 'q'|| video_format =='Q') return quicktime_sample_rate(lav_file->qt_fd,0); #endif return (AVI_audio_rate(lav_file->avi_fd)); } long lav_audio_clips(lav_file_t *lav_file) { if(!lav_file->has_audio) return 0; video_format = lav_file->format; internal_error = 0; /* for error messages */ #ifdef SUPPORT_READ_DV2 if(video_format=='b') return rawdv_audio_bps(lav_file->dv_fd); #endif #ifdef USE_GDK_PIXBUF if(video_format == 'x') return 0; #endif #ifdef HAVE_LIBQUICKTIME if(video_format == 'q'|| video_format == 'Q') return quicktime_audio_length(lav_file->qt_fd,0); #endif return (AVI_audio_bytes(lav_file->avi_fd)/lav_file->bps); } long lav_frame_size(lav_file_t *lav_file, long frame) { video_format = lav_file->format; internal_error = 0; /* for error messages */ #ifdef SUPPORT_READ_DV2 if(video_format == 'b') return rawdv_frame_size( lav_file->dv_fd ); #endif #ifdef USE_GDK_PIXBUF if(video_format == 'x') return output_scale_width * output_scale_height * 3; #endif #ifdef HAVE_LIBQUICKTIME if( video_format == 'q' || video_format == 'Q') return quicktime_frame_size(lav_file->qt_fd,frame,0); #endif return (AVI_frame_size(lav_file->avi_fd,frame)); } int lav_seek_start(lav_file_t *lav_file) { video_format = lav_file->format; internal_error = 0; /* for error messages */ #ifdef SUPPORT_READ_DV2 if(video_format == 'b') return rawdv_set_position( lav_file->dv_fd, 0 ); #endif #ifdef USE_GDK_PIXBUF if(video_format == 'x') return 1; #endif #ifdef HAVE_LIBQUICKTIME return quicktime_seek_start(lav_file->qt_fd); #endif return (AVI_seek_start(lav_file->avi_fd)); } int lav_set_video_position(lav_file_t *lav_file, long frame) { video_format = lav_file->format; internal_error = 0; /* for error messages */ #ifdef SUPPORT_READ_DV2 if(video_format == 'b') return rawdv_set_position( lav_file->dv_fd, frame ); #endif #ifdef USE_GDK_PIXBUF if(video_format == 'x') return 1; #endif #ifdef HAVE_LIBQUICKTIME if(video_format == 'q' || video_format == 'Q') return quicktime_set_video_position(lav_file->qt_fd,(int64_t)frame,0); #endif return (AVI_set_video_position(lav_file->avi_fd,frame)); } int lav_read_frame(lav_file_t *lav_file, uint8_t *vidbuf) { video_format = lav_file->format; internal_error = 0; /* for error messages */ #ifdef SUPPORT_READ_DV2 if(lav_file->format == 'b') { return rawdv_read_frame( lav_file->dv_fd, vidbuf ); } #endif #ifdef USE_GDK_PIXBUF if(lav_file->format == 'x') return -1; #endif #ifdef HAVE_LIBQUICKTIME if(lav_file->format == 'q'|| lav_file->format == 'Q') return quicktime_read_frame(lav_file->qt_fd,vidbuf,0); #endif int kf = 1; int ret = (AVI_read_frame(lav_file->avi_fd,vidbuf,&kf)); /* if(!kf) { // veejay_msg(0, "Requested frame is not a keyframe"); return ret; } */ return ret; } #ifdef USE_GDK_PIXBUF VJFrame *lav_get_frame_ptr( lav_file_t *lav_file ) { if(lav_file->format == 'x') return vj_picture_get( lav_file->picture ); return NULL; } #else uint8_t *lav_get_frame_ptr( lav_file_t *lav_file) { return NULL; } #endif int lav_is_DV(lav_file_t *lav_file) { #ifdef SUPPORT_READ_DV2 if(lav_file->format == 'b') return 1; #endif return 0; } int lav_set_audio_position(lav_file_t *lav_file, long clip) { if(!lav_file->has_audio) return 0; video_format = lav_file->format; internal_error = 0; /* for error messages */ #ifdef SUPPORT_READ_DV2 if(video_format == 'b') return 0; #endif #ifdef USE_GDK_PIXBUF if(video_format == 'x') return 0; #endif #ifdef HAVE_LIBQUICKTIME if(video_format =='q'|| video_format == 'Q' ) { quicktime_set_audio_position(lav_file->qt_fd,clip,0); return 1; } #endif return (AVI_set_audio_position(lav_file->avi_fd,clip*lav_file->bps)); } int lav_read_audio(lav_file_t *lav_file, uint8_t *audbuf, long samps) { if(!lav_file->has_audio) { internal_error = ERROR_NOAUDIO; return -1; } #ifdef SUPPORT_READ_DV2 if(video_format == 'b') return rawdv_read_audio_frame( lav_file->dv_fd, audbuf ); #endif #ifdef USE_GDK_PIXBUF if(video_format == 'x') return 0; #endif video_format = lav_file->format; internal_error = 0; /* for error messages */ #ifdef HAVE_LIBQUICKTIME if( video_format == 'q' || video_format == 'Q') { int64_t last_pos, start_pos; int res, i, j; int16_t *qt_audio = (int16_t *)audbuf, **qt_audion; int channels = lav_audio_channels(lav_file); uint8_t b0, b1; qt_audion = malloc(channels * sizeof (int16_t **)); for (i = 0; i < channels; i++) qt_audion[i] = (int16_t *)malloc(samps * lav_file->bps); start_pos = quicktime_audio_position(lav_file->qt_fd, 0); lqt_decode_audio_track(lav_file->qt_fd, qt_audion, NULL, samps, 0); last_pos = lqt_last_audio_position(lav_file->qt_fd, 0); res = last_pos - start_pos; if (res <= 0) goto out; /* Interleave the channels of audio into the one buffer provided */ for (i =0; i < res; i++) { for (j = 0; j < channels; j++) qt_audio[(channels*i) + j] = qt_audion[j][i]; } if (lav_detect_endian()) { i= 0; while (i < (2*res) ) { b0 = 0; b1 = 0; b0 = (qt_audio[i] & 0x00FF); b1 = (qt_audio[i] & 0xFF00) >> 8; qt_audio[i] = (b0 <<8) + b1; i = i +1; } } out: for (j = 0; j < channels; j++) free(qt_audion[j]); free(qt_audion); return(res); } #endif int res = AVI_read_audio( lav_file->avi_fd, audbuf, (samps * lav_file->bps) ); return res; } int lav_filetype(lav_file_t *lav_file) { return lav_file->format; } lav_file_t *lav_open_input_file(char *filename, long mmap_size) { static char pict[5] = "PICT\0"; char *video_comp = NULL; unsigned char *frame = NULL; int ierr; lav_file_t *lav_fd = (lav_file_t*) vj_calloc(sizeof(lav_file_t)); if(lav_fd==0) { internal_error=ERROR_MALLOC; return 0; } /* Set lav_fd */ #ifdef HAVE_LIBQUICKTIME char *audio_comp; #endif lav_fd->avi_fd = 0; #ifdef SUPPORT_READ_DV2 lav_fd->dv_fd = 0; #endif #ifdef USE_GDK_PIXBUF lav_fd->picture = NULL; #endif lav_fd->format = 0; lav_fd->interlacing = LAV_INTER_UNKNOWN; lav_fd->sar_w = 0; /* (0,0) == unknown */ lav_fd->sar_h = 0; lav_fd->has_audio = 0; lav_fd->bps = 0; lav_fd->is_MJPG = 0; lav_fd->MJPG_chroma = CHROMAUNKNOWN; lav_fd->mmap_size = mmap_size; int ret = 0; /* open file, check if file is a file */ struct stat s; if( stat(filename, &s ) != 0 ) { if(lav_fd) free(lav_fd); veejay_msg(VEEJAY_MSG_ERROR, "Invalid file '%s'. Proper permissions?",filename); return NULL; } if(!S_ISREG( s.st_mode) ) { veejay_msg(VEEJAY_MSG_ERROR, "'%s' is not a regular file",filename); if(lav_fd) free(lav_fd); return NULL; } #ifdef USE_GDK_PIXBUF lav_fd->picture = vj_picture_open( (const char*) filename, output_scale_width, output_scale_height, get_ffmpeg_pixfmt(output_yuv) ); if(lav_fd->picture) { lav_fd->format = 'x'; lav_fd->bogus_len = (int) output_fps; video_comp = pict; ret = 1; veejay_msg(VEEJAY_MSG_DEBUG,"\tLoaded image file"); return lav_fd; } #endif else { lav_fd->avi_fd = AVI_open_input_file(filename,1,mmap_size); if( lav_fd->avi_fd && AVI_errno == AVI_ERR_EMPTY ) { veejay_msg(VEEJAY_MSG_ERROR, "Empty AVI file"); if(lav_fd) free(lav_fd); return NULL; } else if ( lav_fd->avi_fd && AVI_errno == 0 ) { veejay_msg(VEEJAY_MSG_DEBUG, "\tFile is AVI" ); ret =1; } } int alt = 0; if(lav_fd->avi_fd) { ret = 1; alt = 1; lav_fd->format = 'a'; lav_fd->has_audio = (AVI_audio_bits(lav_fd->avi_fd)>0 && AVI_audio_format(lav_fd->avi_fd)==WAVE_FORMAT_PCM); video_comp = AVI_video_compressor(lav_fd->avi_fd); if(video_comp == NULL || strlen(video_comp) <= 0) { veejay_msg(VEEJAY_MSG_ERROR, "Unable to read FOURCC from AVI"); if(lav_fd) free(lav_fd); return NULL; } veejay_msg(VEEJAY_MSG_DEBUG, "\tFOURCC is %s", video_comp ); } else if( AVI_errno==AVI_ERR_NO_AVI || (!lav_fd->avi_fd && !ret) ) { #ifdef HAVE_LIBQUICKTIME if(quicktime_check_sig(filename)) { quicktime_pasp_t pasp; int nfields, detail; lav_fd->qt_fd = quicktime_open(filename,1,0); video_format = 'q'; /* for error messages */ if (!lav_fd->qt_fd) { veejay_msg(VEEJAY_MSG_ERROR, "Unable to open quicktime file"); free(lav_fd); return NULL; } else veejay_msg(VEEJAY_MSG_DEBUG, "\tOpening Quicktime file"); lav_fd->avi_fd = NULL; lav_fd->format = 'q'; video_comp = quicktime_video_compressor(lav_fd->qt_fd,0); veejay_msg(VEEJAY_MSG_DEBUG,"\tFile has fourcc '%s'", video_comp ); /* We want at least one video track */ if (quicktime_video_tracks(lav_fd->qt_fd) < 1) { veejay_msg(VEEJAY_MSG_ERROR, "At least one video track required"); lav_close(lav_fd); internal_error = ERROR_FORMAT; return NULL; } /* * If the quicktime file has the sample aspect atom then use it to set * the sar values in the lav_fd structure. Hardwired (like everywhere else) * to only look at track 0. */ if (lqt_get_pasp(lav_fd->qt_fd, 0, &pasp) != 0) { lav_fd->sar_w = pasp.hSpacing; lav_fd->sar_h = pasp.vSpacing; } /* * If a 'fiel' atom is present (not guaranteed) then use it to set the * interlacing type. */ if (lqt_get_fiel(lav_fd->qt_fd, 0, &nfields, &detail) != 0) { if (nfields == 2) { if (detail == 14 || detail == 6) lav_fd->interlacing = LAV_INTER_BOTTOM_FIRST; else if (detail == 9 || detail == 1) lav_fd->interlacing = LAV_INTER_TOP_FIRST; else veejay_msg(VEEJAY_MSG_DEBUG, "Unknown 'detail' in 'fiel' atom: %d", detail); } else lav_fd->interlacing = LAV_NOT_INTERLACED; } /* Check for audio tracks */ lav_fd->has_audio = 0; if (quicktime_audio_tracks(lav_fd->qt_fd)) { audio_comp = quicktime_audio_compressor(lav_fd->qt_fd,0); if (strncasecmp(audio_comp, QUICKTIME_TWOS,4)==0) lav_fd->has_audio = 1; else veejay_msg(VEEJAY_MSG_WARNING, "Audio compressor '%s' not supported", audio_comp ); } alt = 1; ret = 1; } else veejay_msg(VEEJAY_MSG_DEBUG, "\tNot a Quicktime file"); #endif #ifdef SUPPORT_READ_DV2 if(!alt) { ret = 0; lav_fd->dv_fd = rawdv_open_input_file(filename,mmap_size); if(lav_fd->dv_fd > 0) { lav_fd->MJPG_chroma = rawdv_sampling( lav_fd->dv_fd ); video_comp = rawdv_video_compressor( lav_fd->dv_fd ); lav_fd->format = 'b'; lav_fd->has_audio = 0; ret = 1; alt = 1; veejay_msg(VEEJAY_MSG_DEBUG, "RAW DV file '%s'", video_comp ); } else veejay_msg(VEEJAY_MSG_DEBUG, "\tNot a raw dv file"); } #endif } if(ret == 0 || video_comp == NULL || alt == 0) { free(lav_fd); internal_error = ERROR_FORMAT; return NULL; } lav_fd->bps = (lav_audio_channels(lav_fd)*lav_audio_bits(lav_fd)+7)/8; if(lav_fd->bps==0) lav_fd->bps=1; /* make it save since we will divide by that value */ /* if(strlen(video_comp) == 1 ) { lav_fd->MJPG_chroma = CHROMA422; lav_fd->format = 'V'; lav_fd->interlacing = LAV_NOT_INTERLACED; return lav_fd; } */ #ifdef USE_GDK_PIXBUF if(strncasecmp(video_comp, "PICT",4) == 0 ) { switch(output_yuv) { case FMT_420: case FMT_420F: lav_fd->MJPG_chroma = CHROMA420; break; case FMT_422: case FMT_422F: lav_fd->MJPG_chroma = CHROMA422; break; default: lav_fd->MJPG_chroma = CHROMAUNKNOWN; break; } lav_fd->format = 'x'; lav_fd->interlacing = LAV_NOT_INTERLACED; return lav_fd; } #endif if ( strncasecmp(video_comp,"iyuv",4)==0 || strncasecmp(video_comp,"yv12",4)==0 || strncasecmp(video_comp,"i420",4)==0) { lav_fd->MJPG_chroma = CHROMA420; lav_fd->format = 'Y'; lav_fd->interlacing = LAV_NOT_INTERLACED; return lav_fd; } if(strncasecmp(video_comp,"vj22",4)==0 || strncasecmp(video_comp, "y42b",4) == 0) { lav_fd->MJPG_chroma = CHROMA422F; lav_fd->format = 'V'; lav_fd->interlacing = LAV_NOT_INTERLACED; return lav_fd; } if(strncasecmp(video_comp,"vj20",4)==0) { lav_fd->MJPG_chroma = CHROMA420F; lav_fd->format = 'v'; lav_fd->interlacing = LAV_NOT_INTERLACED; return lav_fd; } if (strncasecmp(video_comp,"yv16",4)==0 || strncasecmp(video_comp,"i422",4)==0 || strncasecmp(video_comp,"hfyu",4)==0) { lav_fd->MJPG_chroma = CHROMA422; lav_fd->format = 'P'; lav_fd->interlacing = LAV_NOT_INTERLACED; return lav_fd; } if( strncasecmp( video_comp, "mlzo", 4 ) == 0 ) { lav_fd->MJPG_chroma = CHROMA422; lav_fd->interlacing = LAV_NOT_INTERLACED; return lav_fd; } if (strncasecmp(video_comp,"dvsd",4)==0 || strncasecmp(video_comp,"dvcp",4) ==0 || strncasecmp(video_comp,"dxsd",4) == 0 || strncasecmp(video_comp,"dvp",3) == 0 || strncasecmp(video_comp,"dvhd",4) == 0 || strncasecmp(video_comp,"dv",2 ) == 0) { int gw = lav_video_height( lav_fd ); if( gw == 480 ) lav_fd->MJPG_chroma = CHROMA411; else lav_fd->MJPG_chroma = CHROMA422; lav_fd->interlacing = LAV_INTER_BOTTOM_FIRST; return lav_fd; } if( strncasecmp(video_comp, "png", 3 ) == 0 || strncasecmp(video_comp, "mpng",4) == 0 ) { lav_fd->MJPG_chroma = CHROMA420; lav_fd->interlacing = LAV_INTER_UNKNOWN; return lav_fd; } if( strncasecmp(video_comp, "svq1", 4 ) == 0 || strncasecmp(video_comp, "svq3", 4 ) == 0 || strncasecmp(video_comp, "rpza", 4 ) == 0 || strncasecmp(video_comp, "cyuv", 4 ) == 0 ) { lav_fd->MJPG_chroma = CHROMA420; lav_fd->interlacing = LAV_INTER_UNKNOWN; return lav_fd; } if (strncasecmp(video_comp,"mjpg", 4) == 0 || strncasecmp(video_comp,"mjpa", 4) == 0 || strncasecmp(video_comp,"jpeg", 4) == 0 || strncasecmp(video_comp,"mjpb" ,4) == 0 || strncasecmp(video_comp,"ljpg", 4) == 0 || strncasecmp(video_comp,"sp5x", 4) == 0 || strncasecmp(video_comp,"jpgl", 4) == 0 || strncasecmp(video_comp,"jfif", 4 ) == 0 || strncasecmp(video_comp,"dmb1", 4)==0 ) { lav_fd->MJPG_chroma = CHROMA420; lav_fd->interlacing = LAV_INTER_UNKNOWN; lav_fd->is_MJPG = 1; if ( lav_set_video_position(lav_fd,0) < 0 ) goto ERREXIT; lav_fd->MJPG_chroma = CHROMAUNKNOWN; lav_fd->interlacing = LAV_INTER_UNKNOWN; return lav_fd; } ierr = ERROR_FORMAT; veejay_msg(VEEJAY_MSG_ERROR, "Unrecognized format '%s'", video_comp); ERREXIT: lav_close(lav_fd); if(frame) free(frame); internal_error = ierr; veejay_msg(VEEJAY_MSG_ERROR, "%s", lav_strerror()); return 0; } const char *lav_strerror(void) { static char error_string[1024]; switch(internal_error) { case ERROR_JPEG: sprintf(error_string,"Internal: broken JPEG format"); internal_error = 0; return error_string; case ERROR_MALLOC: sprintf(error_string,"Internal: Out of memory"); internal_error = 0; return error_string; case ERROR_FORMAT: sprintf(error_string,"Input file format not recognized"); internal_error = 0; return error_string; case ERROR_NOAUDIO: sprintf(error_string,"Trying to read audio from a video only file"); internal_error = 0; return error_string; } switch(video_format) { case 'a': case 'A': case 'Y': case 'v': case 'V': case 'M': case 'P': case 'L': case 'D': return AVI_strerror(); default: /* No or unknown video format */ snprintf(error_string,sizeof(error_string),"No or unknown video format"); return error_string; } } /* static int check_DV2_input(lav_file_t *lav_fd) { int ierr = 0; double len = 0; unsigned char *frame = NULL; lav_fd->is_MJPG = 0; if ( lav_set_video_position(lav_fd,0) ) goto ERREXIT; if ( (len = lav_frame_size(lav_fd,0)) <=0 ) goto ERREXIT; if ( (frame = (unsigned char*) malloc(len)) == 0 ) { ierr=ERROR_MALLOC; goto ERREXIT; } if ( lav_read_frame(lav_fd,frame) <= 0 ) goto ERREXIT; { dv_decoder_t *decoder = dv_decoder_new(0,0,0); dv_parse_header(decoder, frame); switch (decoder->system) { case e_dv_system_525_60: if (dv_format_wide(decoder)) { lav_fd->sar_w = 40; lav_fd->sar_h = 33; } else { lav_fd->sar_w = 10; lav_fd->sar_h = 11; } break; case e_dv_system_625_50: if (dv_format_wide(decoder)) { lav_fd->sar_w = 118; lav_fd->sar_h = 81; } else { lav_fd->sar_w = 59; lav_fd->sar_h = 54; } break; default: lav_fd->sar_w = 0; lav_fd->sar_h = 0; break; } veejay_msg(VEEJAY_MSG_DEBUG, "DV System %s (sar w %d sar h %d)", (decoder->system == e_dv_system_525_60 ? "525-60" : ( decoder->system == e_dv_system_625_50 ? "625-50" : "unknown!")),lav_fd->sar_w,lav_fd->sar_h); dv_decoder_free(decoder); } if ( lav_set_video_position(lav_fd,0) ) goto ERREXIT; return 0; ERREXIT: lav_close(lav_fd); if(frame) free(frame); if (ierr) internal_error = ierr; return 1; } */ int lav_fileno(lav_file_t *lav_file) { int res; video_format = lav_file->format; switch(lav_file->format) { #ifdef HAVE_LIBQUICKTIME case 'q': case 'Q': { #if ( LQT_CODEC_API_VERSION & 0xffff ) > 6 res = lqt_fileno( (quicktime_t*) lav_file->qt_fd ); #else quicktime_t *q = lav_file->qt_fd; res = (int) fileno( (quicktime_t*) q->stream ); #endif } break; #endif default: res = AVI_fileno(lav_file->avi_fd); } return res; } int lav_detect_endian (void) { unsigned int fred; char *pfred; fred = 2 | (1 << (sizeof(int)*8-8)); pfred = (char *)&fred; if (*pfred == 1) return 1; else if(*pfred == 2) return 0; else return -1; }