/* veejay - Linux VeeJay * (C) 2002-2004 Niels Elburg * * * 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. */ /* This file contains code-snippets from the mjpegtools' EditList (C) The Mjpegtools project http://mjpeg.sourceforge.net */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define RUP8(num)(((num)+8)&~8) #ifdef SUPPORT_READ_DV2 #include "rawdv.h" #include "vj-dv.h" #endif #define MAX_CODECS 50 #define CODEC_ID_YUV420 999 #define CODEC_ID_YUV422 998 #define CODEC_ID_YUV422F 997 #define CODEC_ID_YUV420F 996 #define CODEC_ID_YUVLZO 900 #define DUMMY_FRAMES 2 static struct { const char *name; } _chroma_str[] = { { "Unknown" }, // CHROMAUNKNOWN { "4:2:0" }, { "4:2:2" }, { "4:4:4" }, { "4:1:1" }, { "4:2:0 full range" }, { "4:2:2 full range" } }; static long mmap_size = 0; typedef struct { AVCodec *codec; AVCodecContext *codec_ctx; AVFormatContext *avformat_ctx; AVPacket pkt; int pixfmt; int codec_id; } el_decoder_t; extern void sample_new_simple( void *el, long start, long end ); void vj_el_set_mmap_size( long size ) { mmap_size = size; } typedef struct { AVCodec *codec; AVFrame *frame; AVCodecContext *context; uint8_t *tmp_buffer; uint8_t *deinterlace_buffer[3]; VJFrame *img; int fmt; int ref; #ifdef SUPPORT_READ_DV2 vj_dv_decoder *dv_decoder; #endif void *lzo_decoder; } vj_decoder; char vj_el_get_default_norm( float fps ) { if( fps == 25.0f ) return 'p'; if( fps > 23.0f && fps < 24.0f ) return 's'; if( fps > 29.0f && fps <= 30.0f ) return 'n'; return 'p'; } float vj_el_get_default_framerate( int norm ) { switch( norm ) { case VIDEO_MODE_PAL: return 25.0f; case VIDEO_MODE_SECAM: return 23.976f; case VIDEO_MODE_NTSC: return 29.97f; default: veejay_msg(VEEJAY_MSG_WARNING, "Unknown video norm! Use 'p' (PAL), 'n' (NTSC) or 's' (SECAM)"); } return 30.0f; } int vj_el_get_usec_per_frame( float video_fps ) { // norm_usec_per_frame = 1001000 / 30; /* 30ish Hz */ return (int)(1000000 / video_fps); } int vj_el_get_decoder_from_fourcc( const char *fourcc ) { return avhelper_get_codec_by_name( fourcc ); } static void _el_free_decoder( vj_decoder *d ) { if(d) { if(d->tmp_buffer) free( d->tmp_buffer ); if(d->deinterlace_buffer[0]) free(d->deinterlace_buffer[0]); #ifdef SUPPORT_READ_DV2 if( d->dv_decoder ) { vj_dv_free_decoder( d->dv_decoder ); } #endif if(d->frame) { av_free(d->frame); } if(d->img) free(d->img); if( d->lzo_decoder ) lzo_free(d->lzo_decoder); free(d); } d = NULL; } #define LARGE_NUM (256*256*256*64) static int el_pixel_format_org = 1; static int el_pixel_format_ = 1; static int el_width_ = 0; static float el_fps_ = 30; static int el_height_ = 0; static long mem_chunk_ = 0; static int el_switch_jpeg_ = 0; static VJFrame *el_out_ = NULL; long vj_el_get_mem_size() { return mem_chunk_; } void vj_el_init_chunk(int size) { //@@ chunk size per editlist mem_chunk_ = 1024 * size; } static int require_same_resolution = 0; void vj_el_init(int pf, int switch_jpeg, int dw, int dh, float fps) { el_pixel_format_org = pf; el_pixel_format_ = get_ffmpeg_pixfmt( pf ); el_width_ = dw; el_height_ = dh; el_fps_ = fps; el_switch_jpeg_ = switch_jpeg; lav_set_project( dw,dh, fps, pf ); el_out_ = yuv_yuv_template( NULL,NULL,NULL, dw,dh, get_ffmpeg_pixfmt(pf) ); char *maxFileSize = getenv( "VEEJAY_MAX_FILESIZE" ); if( maxFileSize != NULL ) { uint64_t mfs = atol( maxFileSize ); if( mfs > AVI_get_MAX_LEN() ) mfs = AVI_get_MAX_LEN(); if( mfs > 0 ) { AVI_set_MAX_LEN( mfs ); veejay_msg(VEEJAY_MSG_INFO, "Changed maximum file size" ); } } if( has_env_setting( "VEEJAY_RUN_MODE", "CLASSIC" ) ) { require_same_resolution = 1; } veejay_msg(VEEJAY_MSG_DEBUG,"Initialized EDL, processing video in %d x %d", el_width_, el_height_ ); } int vj_el_is_dv(editlist *el) { #ifdef SUPPORT_READ_DV2 return is_dv_resolution(el->video_width, el->video_height); #else return 0; #endif } void vj_el_prepare() { // reset_cache( el->cache ); } void vj_el_break_cache( editlist *el ) { if(el) { if( el->cache ) free_cache( el->cache ); el->cache = NULL; } } static int never_cache_ = 0; void vj_el_set_caching(int status) { never_cache_ = status; } void vj_el_setup_cache( editlist *el ) { if(!el->cache && !never_cache_) { int n_slots = mem_chunk_ / el->max_frame_size; if( el->video_frames < n_slots) { veejay_msg(VEEJAY_MSG_DEBUG, "Good, can load this sample entirely into memory... (%d slots, chunk=%d, framesize=%d)", n_slots, mem_chunk_, el->max_frame_size ); el->cache = init_cache( n_slots ); } } } void vj_el_clear_cache( editlist *el ) { if( el != NULL ) { if(el->cache) { reset_cache(el->cache); } } } void vj_el_deinit() { } int vj_el_cache_size() { return cache_avail_mb(); } #ifndef GREMLIN_GUARDIAN #define GREMLIN_GUARDIAN (128*1024)-1 #endif vj_decoder *_el_new_decoder( void *ctx, int id , int width, int height, float fps, int pixel_format, int out_fmt, long max_frame_size) { vj_decoder *d = (vj_decoder*) vj_calloc(sizeof(vj_decoder)); if(!d) return NULL; #ifdef SUPPORT_READ_DV2 if( id == CODEC_ID_DVVIDEO ) d->dv_decoder = vj_dv_decoder_init(1, width, height, out_fmt ); #endif if( id == CODEC_ID_YUVLZO ) { d->lzo_decoder = lzo_new(); } else if ( id == CODEC_ID_YUV422 || id == CODEC_ID_YUV420 || id == CODEC_ID_YUV420F || id == CODEC_ID_YUV422F ) { } else if( ctx ) { d->codec = avhelper_get_codec(ctx); d->context = avhelper_get_codec_ctx(ctx); d->frame = avhelper_alloc_frame(); d->img = (VJFrame*) vj_calloc(sizeof(VJFrame)); d->img->width = width; d->img->height = height; } size_t safe_max_frame_size = (max_frame_size < GREMLIN_GUARDIAN) ? 128 * 1024: RUP8(max_frame_size); d->tmp_buffer = (uint8_t*) vj_malloc( sizeof(uint8_t) * safe_max_frame_size ); d->fmt = id; return d; } int get_ffmpeg_pixfmt( int pf ) { switch( pf ) { case FMT_420: return PIX_FMT_YUV420P; case FMT_420F: return PIX_FMT_YUVJ420P; case FMT_422: return PIX_FMT_YUV422P; case FMT_422F: return PIX_FMT_YUVJ422P; case 4: return PIX_FMT_YUV444P; } return PIX_FMT_YUV422P; } int get_ffmpeg_shift_size(int fmt) { return 0; } static long get_max_frame_size( lav_file_t *fd ) { long total_frames = lav_video_frames( fd ); long i; long res = 0; for (i = 0; i < total_frames; i++) { long tmp = lav_frame_size( fd, i ); if( tmp > res ) { res = tmp; } } return (((res)+8)&~8); } int open_video_file(char *filename, editlist * el, int preserve_pathname, int deinter, int force, char override_norm, int out_format, int width, int height ) { int i, n, nerr; int chroma=0; int _fc; int decoder_id = 0; const char *compr_type; char *realname = NULL; if( filename == NULL ) { veejay_msg(VEEJAY_MSG_ERROR, "No files to open!"); return -1; } if (preserve_pathname) realname = vj_strdup(filename); else realname = canonicalize_file_name( filename ); if(realname == NULL ) { veejay_msg(VEEJAY_MSG_ERROR, "Cannot get full path of '%s'", filename); return -1; } for (i = 0; i < el->num_video_files; i++) { if (strncmp(realname, el->video_file_list[i], strlen( el->video_file_list[i])) == 0) { veejay_msg(VEEJAY_MSG_ERROR, "File %s already in editlist", realname); if(realname) free(realname); return -1; } } if (el->num_video_files >= MAX_EDIT_LIST_FILES) { // mjpeg_error_exit1("Maximum number of video files exceeded"); veejay_msg(VEEJAY_MSG_ERROR,"Maximum number of video files exceeded\n"); if(realname) free(realname); return -1; } if (el->num_video_files >= 1) chroma = el->MJPG_chroma; n = el->num_video_files; int pixfmt = -1; lav_file_t *elfd = lav_open_input_file(filename,mmap_size ); el->lav_fd[n] = NULL; if (elfd == NULL) { veejay_msg(VEEJAY_MSG_ERROR,"Error loading videofile '%s'", realname); veejay_msg(VEEJAY_MSG_ERROR,"%s",lav_strerror()); if(realname) free(realname); return -1; } if( !elfd->picture ) el->ctx[n] = avhelper_get_decoder( filename, out_format, width, height ); if( el->ctx[n] == NULL ) { pixfmt = test_video_frame( el, n, elfd, el_pixel_format_ ); if( pixfmt == -1 ) { veejay_msg(VEEJAY_MSG_ERROR, "Unable to determine format of %s", filename ); lav_close(elfd); if(realname) free(realname); return -1; } el->pixfmt[n] = pixfmt; } else { el_decoder_t *x = (el_decoder_t*) el->ctx[n]; el->pixfmt[n] = x->pixfmt; } if(lav_video_frames(elfd) < 1) { veejay_msg(VEEJAY_MSG_ERROR, "Cowardly refusing to load empty video files"); if(realname) free(realname); lav_close(elfd); if( el->ctx[n] ) avhelper_close_decoder( el->ctx[n] ); return -1; } _fc = lav_video_MJPG_chroma(elfd); if( !(_fc == CHROMA422 || _fc == CHROMA420 || _fc == CHROMA444 || _fc == CHROMAUNKNOWN || _fc == CHROMA411 || _fc == CHROMA422F || _fc == CHROMA420F)) { veejay_msg(VEEJAY_MSG_ERROR,"Input file %s is not in a valid format (%d)",filename,_fc); if(realname) free(realname); lav_close( elfd ); if( el->ctx[n] ) avhelper_close_decoder( el->ctx[n] ); return -1; } if(chroma == CHROMAUNKNOWN) { /* set chroma */ el->MJPG_chroma = _fc; chroma = _fc; //FIXME } el->lav_fd[n] = elfd; el->num_frames[n] = lav_video_frames(el->lav_fd[n]); el->video_file_list[n] = vj_strndup(realname, strlen(realname)); /* Debug Output */ if(n == 0 ) { veejay_msg(VEEJAY_MSG_DEBUG,"\tFull name: %s", filename, realname); veejay_msg(VEEJAY_MSG_DEBUG,"\tFrames: %ld", lav_video_frames(el->lav_fd[n])); veejay_msg(VEEJAY_MSG_DEBUG,"\tWidth: %d", lav_video_width(el->lav_fd[n])); veejay_msg(VEEJAY_MSG_DEBUG,"\tHeight: %d", lav_video_height(el->lav_fd[n])); const char *int_msg; switch (lav_video_interlacing(el->lav_fd[n])) { case LAV_NOT_INTERLACED: int_msg = "Not interlaced"; break; case LAV_INTER_TOP_FIRST: int_msg = "Top field first"; break; case LAV_INTER_BOTTOM_FIRST: int_msg = "Bottom field first"; break; default: int_msg = "Unknown!"; break; } if( deinter == 1 && (lav_video_interlacing(el->lav_fd[n]) != LAV_NOT_INTERLACED)) el->auto_deinter = 1; veejay_msg(VEEJAY_MSG_DEBUG,"\tFrames/sec: %f", lav_frame_rate(el->lav_fd[n])); veejay_msg(VEEJAY_MSG_DEBUG,"\tSampling format: %s", _chroma_str[ lav_video_MJPG_chroma(el->lav_fd[n])].name); veejay_msg(VEEJAY_MSG_DEBUG,"\tVideo compressor: %s",lav_video_compressor(el->lav_fd[n])); veejay_msg(VEEJAY_MSG_DEBUG,"\tAudio samps: %ld", lav_audio_clips(el->lav_fd[n])); veejay_msg(VEEJAY_MSG_DEBUG,"\tAudio chans: %d", lav_audio_channels(el->lav_fd[n])); veejay_msg(VEEJAY_MSG_DEBUG,"\tAudio bits: %d", lav_audio_bits(el->lav_fd[n])); veejay_msg(VEEJAY_MSG_DEBUG,"\tAudio rate: %ld", lav_audio_rate(el->lav_fd[n])); } else { veejay_msg(VEEJAY_MSG_DEBUG, "\tFull name %s",realname); veejay_msg(VEEJAY_MSG_DEBUG, "\tFrames %d", lav_video_frames(el->lav_fd[n])); veejay_msg(VEEJAY_MSG_DEBUG, "\tDecodes into %s", _chroma_str[ lav_video_MJPG_chroma( el->lav_fd[n]) ]); } nerr = 0; if (n == 0) { /* First file determines parameters */ el->video_height = lav_video_height(el->lav_fd[n]); el->video_width = lav_video_width(el->lav_fd[n]); el->video_inter = lav_video_interlacing(el->lav_fd[n]); el->video_fps = lav_frame_rate(el->lav_fd[n]); lav_video_clipaspect(el->lav_fd[n], &el->video_sar_width, &el->video_sar_height); if (!el->video_norm) { el->video_norm = vj_el_get_default_norm( el->video_fps ); } if (!el->video_norm) { if(override_norm == 'p' || override_norm == 'n' || override_norm == 's') el->video_norm = override_norm; } if( !el->video_norm ) { veejay_msg(VEEJAY_MSG_ERROR, "Unable to detect video norm, using PAL" ); el->video_norm = 'p'; } if(!el->is_empty) { el->audio_chans = lav_audio_channels(el->lav_fd[n]); if (el->audio_chans > 2) { veejay_msg(VEEJAY_MSG_ERROR, "File %s has %d audio channels - cant play that!", filename,el->audio_chans); nerr++; } el->has_audio = (el->audio_chans == 0 ? 0: 1); el->audio_bits = lav_audio_bits(el->lav_fd[n]); el->audio_rate = lav_audio_rate(el->lav_fd[n]); el->audio_bps = (el->audio_bits * el->audio_chans + 7) / 8; } else { if(lav_audio_channels(el->lav_fd[n]) != el->audio_chans || lav_audio_rate(el->lav_fd[n]) != el->audio_rate || lav_audio_bits(el->lav_fd[n]) != el->audio_bits ) { veejay_msg(VEEJAY_MSG_ERROR,"File %s has different audio properties - cant play that!"); veejay_msg(VEEJAY_MSG_DEBUG,"Audio rate %ld, source is %ld", el->audio_rate, lav_audio_rate(el->lav_fd[n])); veejay_msg(VEEJAY_MSG_DEBUG,"Audio bits %d, source is %d", el->audio_bits, lav_audio_bits(el->lav_fd[n])); veejay_msg(VEEJAY_MSG_DEBUG,"Audio channels %d, source is %d", el->audio_chans, lav_audio_channels(el->lav_fd[n]) ); nerr++; } else el->has_audio = 1; } } else { /* All files after first have to match the paramters of the first */ if (el->video_height != lav_video_height(el->lav_fd[n]) || el->video_width != lav_video_width(el->lav_fd[n])) { veejay_msg( (require_same_resolution ? VEEJAY_MSG_ERROR: VEEJAY_MSG_WARNING), "File %s: Geometry %dx%d does not match %dx%d (performance penalty).", filename, lav_video_width(el->lav_fd[n]), lav_video_height(el->lav_fd[n]), el->video_width, el->video_height); if( require_same_resolution ) nerr++; } /* give a warning on different fps instead of error , this is better for live performances */ if (fabs(el->video_fps - lav_frame_rate(el->lav_fd[n])) > 0.0000001) { veejay_msg(VEEJAY_MSG_WARNING,"File %s: fps is %3.2f , but playing at %3.2f", filename, lav_frame_rate(el->lav_fd[n]), el->video_fps); } /* If first file has no audio, we don't care about audio */ if (el->has_audio) { if( el->audio_rate < 44000 ) { veejay_msg(VEEJAY_MSG_ERROR, "File %s: Cannot play %d Hz audio. Use at least 44100 Hz or start with -a0", filename, el->audio_rate); nerr++; } else { if (el->audio_chans != lav_audio_channels(el->lav_fd[n]) || el->audio_bits != lav_audio_bits(el->lav_fd[n]) || el->audio_rate != lav_audio_rate(el->lav_fd[n])) { int err_level = VEEJAY_MSG_ERROR; if( lav_audio_rate(el->lav_fd[n]) == 0 ) err_level = VEEJAY_MSG_WARNING; veejay_msg(err_level,"File %s: Mismatched audio properties: %d channels , %d bit %ld Hz", filename, lav_audio_channels(el->lav_fd[n]), lav_audio_bits(el->lav_fd[n]), lav_audio_rate(el->lav_fd[n]) ); if( err_level == VEEJAY_MSG_ERROR ) nerr++; } } } if (nerr) { if(el->lav_fd[n]) lav_close( el->lav_fd[n] ); el->lav_fd[n] = NULL; if(realname) free(realname); if(el->video_file_list[n]) free(el->video_file_list[n]); el->video_file_list[n] = NULL; if( el->ctx[n] ) avhelper_close_decoder( el->ctx[n] ); return -1; } } compr_type = (const char*) lav_video_compressor(el->lav_fd[n]); if(!compr_type) { veejay_msg(VEEJAY_MSG_ERROR, "Unable to read codec information from file"); if(el->lav_fd[n]) lav_close( el->lav_fd[n] ); el->lav_fd[n] = NULL; if(realname) free(realname); if(el->video_file_list[n]) free(el->video_file_list[n]); el->video_file_list[n] = NULL; if( el->ctx[n] ) avhelper_close_decoder( el->ctx[n] ); return -1; } if( el->decoders[n] == NULL ) { long max_frame_size = get_max_frame_size( el->lav_fd[n] ); decoder_id = avhelper_get_codec_by_name( compr_type ); el->decoders[n] = _el_new_decoder( el->ctx[n], decoder_id, el->video_width, el->video_height, el->video_fps, el->pixfmt[ n ],el_pixel_format_, max_frame_size ); if( el->decoders[n] == NULL ) { veejay_msg(VEEJAY_MSG_ERROR,"Unsupported video compression type: %s", compr_type ); if( el->lav_fd[n] ) lav_close( el->lav_fd[n] ); el->lav_fd[n] = NULL; if( realname ) free(realname ); if( el->video_file_list[n]) free(el->video_file_list[n]); el->video_file_list[n] = NULL; if( el->ctx[n] ) avhelper_close_decoder( el->ctx[n] ); return -1; } } if(realname) free(realname); if(el->is_empty) { el->video_frames = el->num_frames[0]; el->video_frames -= DUMMY_FRAMES; } el->is_empty = 0; el->has_video = 1; el->num_video_files ++; if( el_width_ == 0 && el_height_ == 0 ) { el_width_ = el->video_width; el_height_ = el->video_height; el_fps_ = el->video_fps; veejay_msg(VEEJAY_MSG_WARNING, "Initialized video project settings from first file (%s)" , filename ); } return n; } static int vj_el_dummy_frame( uint8_t *dst[3], editlist *el ,int pix_fmt) { const int uv_len = (el->video_width * el->video_height) / ( ( (pix_fmt==FMT_422||pix_fmt==FMT_422F) ? 2 : 4)); const int len = el->video_width * el->video_height; veejay_memset( dst[0], 16, len ); veejay_memset( dst[1],128, uv_len ); veejay_memset( dst[2],128, uv_len ); return 1; } int vj_el_get_file_fourcc(editlist *el, int num, char *fourcc) { if(num >= el->num_video_files) return 0; if( fourcc == NULL) return 0; const char *compr = lav_video_compressor( el->lav_fd[num] ); if(compr == NULL) return 0; snprintf(fourcc,4,"%s", compr ); fourcc[5] = '\0'; return 1; } int vj_el_bogus_length( editlist *el, long nframe ) { uint64_t n = 0; if(! el) return 0; if( !el->has_video || el->is_empty ) return 0; if( nframe < 0 ) nframe = 0; else if (nframe > el->total_frames ) nframe = el->total_frames; n = el->frame_list[nframe]; return lav_bogus_video_length( el->lav_fd[ N_EL_FILE(n) ] ); } int vj_el_set_bogus_length( editlist *el, long nframe, int len ) { uint64_t n = 0; if( len <= 0 ) return 0; if( !el->has_video || el->is_empty ) return 0; if (nframe < 0) nframe = 0; if (nframe > el->total_frames) nframe = el->total_frames; n = el->frame_list[nframe]; if( !lav_bogus_video_length( el->lav_fd[N_EL_FILE(n)] ) ) return 0; lav_bogus_set_length( el->lav_fd[N_EL_FILE(n)], len ); return 1; } int vj_el_get_video_frame(editlist *el, long nframe, uint8_t *dst[4]) { if( el->has_video == 0 || el->is_empty ) { vj_el_dummy_frame( dst, el, el->pixel_format ); return 2; } int res = 0; uint64_t n; int out_pix_fmt = el->pixel_format; int in_pix_fmt = out_pix_fmt; if (nframe < 0) nframe = 0; if (nframe > el->total_frames) nframe = el->total_frames; if( nframe < 0 || nframe > el->total_frames ) { return 0; } n = el->frame_list[nframe]; in_pix_fmt = el->pixfmt[N_EL_FILE(n)]; uint8_t *in_cache = NULL; if(el->cache) in_cache = get_cached_frame( el->cache, nframe, &res, &in_pix_fmt ); int decoder_id = lav_video_compressor_type( el->lav_fd[N_EL_FILE(n)] ); if(! in_cache ) { res = lav_set_video_position(el->lav_fd[N_EL_FILE(n)], N_EL_FRAME(n)); if (res < 0) { veejay_msg(VEEJAY_MSG_ERROR,"Error setting video position: %s", lav_strerror()); return -1; } } if( decoder_id == 0xffff ) { VJFrame *srci = lav_get_frame_ptr( el->lav_fd[ N_EL_FILE(n) ] ); if( srci == NULL ) { veejay_msg(VEEJAY_MSG_ERROR, "Error decoding Image %ld", N_EL_FRAME(n)); return -1; } int strides[4] = { el_out_->len, el_out_->uv_len, el_out_->uv_len,0 }; vj_frame_copy( srci->data, dst, strides ); return 1; } vj_decoder *d = (vj_decoder*) el->decoders[ N_EL_FILE(n) ]; if(!in_cache) { if(lav_filetype( el->lav_fd[N_EL_FILE(n)] ) != 'x') { res = lav_read_frame(el->lav_fd[N_EL_FILE(n)], d->tmp_buffer); if(res > 0 && el->cache) cache_frame( el->cache, d->tmp_buffer, res, nframe, decoder_id ); } } uint8_t *data = ( in_cache == NULL ? d->tmp_buffer: in_cache ); int inter = 0; uint8_t *in[3] = { NULL,NULL,NULL }; int strides[4] = { el_out_->len, el_out_->uv_len, el_out_->uv_len ,0}; uint8_t *dataplanes[4] = { data , data + el_out_->len, data + el_out_->len + el_out_->uv_len,0 }; switch( decoder_id ) { case CODEC_ID_YUV420: vj_frame_copy1( data,dst[0], el_out_->len ); in[0] = data; in[1] = data+el_out_->len; in[2] = data+el_out_->len + (el_out_->len/4); if( el_pixel_format_ == PIX_FMT_YUVJ422P ) { yuv_scale_pixels_from_ycbcr( in[0],16.0f,235.0f, el_out_->len ); yuv_scale_pixels_from_ycbcr( in[1],16.0f,240.0f, el_out_->len/4); } yuv420to422planar( in , dst, el->video_width,el->video_height ); return 1; break; case CODEC_ID_YUV420F: vj_frame_copy1( data, dst[0], el_out_->len); in[0] = data; in[1] = data + el_out_->len; in[2] = data + el_out_->len+(el_out_->len/4); if( el_pixel_format_ == PIX_FMT_YUV422P ) { yuv_scale_pixels_from_y( dst[0], el_out_->len ); yuv_scale_pixels_from_uv( dst[1], el_out_->len/4); } yuv420to422planar( in , dst, el->video_width,el->video_height ); return 1; break; case CODEC_ID_YUV422: vj_frame_copy( dataplanes,dst,strides ); if( el_pixel_format_ == PIX_FMT_YUVJ422P ) { yuv_scale_pixels_from_ycbcr( dst[0],16.0f,235.0f, el_out_->len ); yuv_scale_pixels_from_ycbcr( dst[1],16.0f,240.0f, el_out_->len/2); } return 1; break; case CODEC_ID_YUV422F: vj_frame_copy( dataplanes, dst, strides ); if( el_pixel_format_ == PIX_FMT_YUV422P ) { yuv_scale_pixels_from_y( dst[0], el_out_->len ); yuv_scale_pixels_from_uv( dst[1], el_out_->len/2); } return 1; break; case CODEC_ID_YUVLZO: if( ( in_pix_fmt == PIX_FMT_YUVJ420P || in_pix_fmt == PIX_FMT_YUV420P ) ) { inter = lzo_decompress420into422(d->lzo_decoder, data,res,dst, el->video_width,el->video_height ); } else { inter = lzo_decompress_el( d->lzo_decoder, data,res, dst,el->video_width*el->video_height); } return inter; break; default: return avhelper_decode_video( el->ctx[ N_EL_FILE(n) ], data, res, dst ); break; } return 0; } int test_video_frame( editlist *el, int n, lav_file_t *lav,int out_pix_fmt) { int in_pix_fmt = 0; int res = lav_set_video_position( lav, 0); if( res < 0 ) { veejay_msg(VEEJAY_MSG_ERROR, "Error setting frame 0: %s", lav_strerror()); return -1; } int decoder_id = lav_video_compressor_type( lav ); if( decoder_id < 0 ) { veejay_msg(VEEJAY_MSG_ERROR, "Cannot play that file, unsupported codec"); return -1; } if(lav_filetype( lav ) == 'x') { return out_pix_fmt; } switch( lav->MJPG_chroma ) { case CHROMA420F: in_pix_fmt = PIX_FMT_YUVJ420P;break; case CHROMA422F: in_pix_fmt = PIX_FMT_YUVJ422P;break; case CHROMA420: in_pix_fmt = PIX_FMT_YUV420P; break; case CHROMA422: case CHROMA411: in_pix_fmt = PIX_FMT_YUV422P; break; default: veejay_msg(0 ,"Unsupported pixel format"); break; } long max_frame_size = get_max_frame_size( lav ); vj_decoder *d = _el_new_decoder( NULL, decoder_id, lav_video_width( lav), lav_video_height( lav), (float) lav_frame_rate( lav ), in_pix_fmt, out_pix_fmt, max_frame_size ); if(!d) { return -1; } res = lav_read_frame( lav, d->tmp_buffer); if( res <= 0 ) { veejay_msg(VEEJAY_MSG_ERROR, "Error reading frame: %s", lav_strerror()); _el_free_decoder( d ); return -1; } int ret = -1; switch( decoder_id ) { case CODEC_ID_YUV420F: ret = PIX_FMT_YUVJ420P; break; case CODEC_ID_YUV422F: ret = PIX_FMT_YUVJ422P; break; case CODEC_ID_YUV420: ret = PIX_FMT_YUV420P; break; case CODEC_ID_YUV422: ret = PIX_FMT_YUV422P; break; case CODEC_ID_DVVIDEO: #ifdef SUPPORT_READ_DV2 ret = vj_dv_scan_frame( d->dv_decoder, d->tmp_buffer ); if( ret == PIX_FMT_YUV420P || ret == PIX_FMT_YUVJ420P ) lav->MJPG_chroma = CHROMA420; else lav->MJPG_chroma = CHROMA422; #endif break; case CODEC_ID_YUVLZO: ret = PIX_FMT_YUVJ422P; if ( in_pix_fmt != ret ) { //@ correct chroma if( ret == PIX_FMT_YUV420P || ret == PIX_FMT_YUVJ420P ) lav->MJPG_chroma = CHROMA420; else lav->MJPG_chroma = CHROMA422; } break; default: _el_free_decoder( d ); return -1; break; } el->decoders[n] = (void*) d; return ret; } int vj_el_get_audio_frame(editlist *el, uint32_t nframe, uint8_t *dst) { int ret = 0; uint64_t n; int ns0, ns1; if(el->is_empty) { int ns = el->audio_rate / el->video_fps; veejay_memset( dst, 0, sizeof(uint8_t) * ns * el->audio_bps ); return 1; } if (!el->has_audio) return 0; if (nframe < 0) nframe = 0; if (nframe > el->total_frames) nframe = el->total_frames; n = el->frame_list[nframe]; ns1 = (double) (N_EL_FRAME(n) + 1) * el->audio_rate / el->video_fps; ns0 = (double) N_EL_FRAME(n) * el->audio_rate / el->video_fps; ret = lav_set_audio_position(el->lav_fd[N_EL_FILE(n)], ns0); if (ret < 0) { veejay_msg(0,"Unable to seek to frame position %ld", ns0); return -1; } ret = lav_read_audio(el->lav_fd[N_EL_FILE(n)], dst, (ns1 - ns0)); if (ret < 0) { veejay_msg(0, "Error reading audio data at frame position %ld", ns0); int ns = el->audio_rate / el->video_fps; veejay_memset( dst, 0, sizeof(uint8_t) * ns * el->audio_bps ); return 1; } return (ns1 - ns0); } int vj_el_init_420_frame(editlist *el, VJFrame *frame) { frame->data[0] = NULL; frame->data[1] = NULL; frame->data[2] = NULL; frame->uv_len = (el->video_width>>1) * (el->video_height>>1); frame->uv_width = el->video_width >> 1; frame->uv_height = el->video_height >> 1; frame->len = el->video_width * el->video_height; frame->shift_v = 1; frame->shift_h = 1; frame->width = el->video_width; frame->height = el->video_height; frame->ssm = 0; frame->stride[0] = el->video_width; frame->stride[1] = frame->stride[2] = frame->stride[0]/2; frame->format = el_pixel_format_; return 1; } int vj_el_init_422_frame(editlist *el, VJFrame *frame) { frame->data[0] = NULL; frame->data[1] = NULL; frame->data[2] = NULL; frame->uv_len = (el->video_width>>1) * (el->video_height); frame->uv_width = el->video_width >> 1; frame->uv_height = el->video_height; frame->len = el->video_width * el->video_height; frame->shift_v = 0; frame->shift_h = 1; frame->width = el->video_width; frame->height = el->video_height; frame->ssm = 0; frame->stride[0] = el->video_width; frame->stride[1] = frame->stride[2] = frame->stride[0]/2; frame->format = el_pixel_format_; return 1; } int vj_el_get_audio_frame_at(editlist *el, uint32_t nframe, uint8_t *dst, int num ) { // get audio from current frame + n frames int ret = 0; uint64_t n; int ns0, ns1; if (!el->has_audio) return 0; if (!el->has_video) { int size = el->audio_rate / el->video_fps * el->audio_bps; veejay_memset(dst,0,size); return size; } if (nframe < 0) nframe = 0; if (nframe > el->total_frames) nframe = el->total_frames; n = el->frame_list[nframe]; ns1 = (double) (N_EL_FRAME(n) + num) * el->audio_rate / el->video_fps; ns0 = (double) N_EL_FRAME(n) * el->audio_rate / el->video_fps; ret = lav_set_audio_position(el->lav_fd[N_EL_FILE(n)], ns0); if (ret < 0) return -1; ret = lav_read_audio(el->lav_fd[N_EL_FILE(n)], dst, (ns1 - ns0)); if (ret < 0) return -1; return (ns1 - ns0); } editlist *vj_el_dummy(int flags, int deinterlace, int chroma, char norm, int width, int height, float fps, int fmt) { editlist *el = vj_calloc(sizeof(editlist)); if(!el) { return NULL; } el->MJPG_chroma = chroma; el->video_norm = norm; el->is_empty = 1; el->is_clone = 1; el->has_audio = 0; el->audio_rate = 0; el->audio_bits = 0; el->audio_bps = 0; el->audio_chans = 0; el->num_video_files = 0; el->video_width = width; el->video_height = height; el->video_frames = DUMMY_FRAMES; el->total_frames = el->video_frames - 1; el->video_fps = fps; el->video_inter = LAV_NOT_INTERLACED; el->pixel_format = get_ffmpeg_pixfmt(fmt); /* output pixel format */ if( fmt == -1 ) el->pixel_format = el_pixel_format_; el->pixel_format = fmt; el->auto_deinter = deinterlace; el->max_frame_size = width * height * 3; el->last_afile = -1; el->last_apos = 0; el->frame_list = NULL; el->has_video = 0; el->cache = NULL; return el; } void vj_el_scan_video_file( char *filename, int *dw, int *dh, float *dfps, long *arate ) { void *tmp = avhelper_get_decoder( filename, PIX_FMT_YUVJ422P, -1, -1 ); if( tmp ) { AVCodecContext *c = avhelper_get_codec_ctx( tmp ); *dw = c->width; *dh = c->height; if( c->time_base.num > 0 ) { *dfps = (float) c->time_base.den / c->time_base.num; } else { veejay_msg(VEEJAY_MSG_WARNING,"Unable to detect frame rate in %s", filename ); } *arate = c->sample_rate; avhelper_close_decoder(tmp); } else { lav_file_t *fd = lav_open_input_file( filename, mmap_size ); if( fd ) { *dw = lav_video_width( fd ); *dh = lav_video_height( fd ); *dfps = lav_frame_rate( fd ); *arate = lav_audio_rate( fd ); lav_close(fd); } } veejay_msg(VEEJAY_MSG_DEBUG, "Using video settings from first loaded video %s: %dx%d@%2.2f", filename,*dw,*dh,*dfps); } int vj_el_auto_detect_scenes( editlist *el, uint8_t *tmp[4], int w, int h, int dl_threshold ) { long n1 = 0; long n2 = el->total_frames; long n; int dl = 0; int last_lm = 0; int index = 0; long prev = 0; if( el == NULL || el->is_empty || el->total_frames < 2 ) return 0; for( n = n1; n < n2; n ++ ) { vj_el_get_video_frame(el, n, tmp ); int lm = luminance_mean( tmp, w, h ); if( n == 0 ) { dl = 0; } else { dl = abs( lm - last_lm ); } last_lm = lm; veejay_msg(VEEJAY_MSG_DEBUG,"frame %ld/%ld luminance mean %d, delta %d ", n, n2, lm, dl ); if( dl > dl_threshold ) { if( prev == 0 ) { sample_new_simple(el,0,n); veejay_msg(VEEJAY_MSG_INFO,"sampled frames %ld - %ld", 0,n); } else { sample_new_simple(el,prev,n); veejay_msg(VEEJAY_MSG_INFO,"sampled frames %ld - %ld", prev, n ); } prev = n; index ++; } } return index; } editlist *vj_el_init_with_args(char **filename, int num_files, int flags, int deinterlace, int force,char norm , int out_format, int width, int height) { editlist *el = vj_calloc(sizeof(editlist)); FILE *fd; uint64_t index_list[MAX_EDIT_LIST_FILES]; int num_list_files; long i,nf=0; int n1=0; int n2=0; long nl=0; uint64_t n =0; int av_pixfmt = get_ffmpeg_pixfmt( out_format ); if(!el) return NULL; el->has_video = 1; //assume we get it el->MJPG_chroma = CHROMA420; el->is_empty = 0; if(!filename[0] || filename == NULL) { veejay_msg(VEEJAY_MSG_ERROR,"\tInvalid filename given"); free(el); return NULL; } if(strcmp(filename[0], "+p" ) == 0 || strcmp(filename[0], "+n") == 0 || strcmp(filename[0],"+s") == 0 ) { el->video_norm = filename[0][1]; nf = 1; } if(force) { veejay_msg(VEEJAY_MSG_WARNING, "Forcing load on interlacing and gop_size"); } for (; nf < num_files; nf++) { /* Check if file really exists, is mounted etc... */ struct stat fileinfo; if(stat( filename[nf], &fileinfo)!= 0) { veejay_msg(VEEJAY_MSG_ERROR, "Unable to access file '%s'",filename[nf] ); vj_el_free(el); return NULL; } char line[1024]; veejay_memset(line,0,sizeof(line)); fd = fopen(filename[nf], "r"); if (fd == NULL) { veejay_msg(VEEJAY_MSG_DEBUG,"Error opening %s:", filename[nf]); fclose(fd); vj_el_free(el); return NULL; } if( fgets(line, 1024, fd) == NULL ) { fclose(fd); veejay_msg(VEEJAY_MSG_DEBUG,"Error opening %s:", filename[nf]); vj_el_free(el); return NULL; } if (strcmp(line, "LAV Edit List\n") == 0) { /* Ok, it is a edit list */ veejay_msg(VEEJAY_MSG_DEBUG, "Edit list %s opened", filename[nf]); /* Read second line: Video norm */ fgets(line, 1024, fd); if (line[0] != 'N' && line[0] != 'n' && line[0] != 'P' && line[0] != 'p' && line[0] != 's' && line[0] != 'S') { veejay_msg(VEEJAY_MSG_DEBUG,"Edit list second line is not NTSC/PAL/SECAM"); fclose(fd); vj_el_free(el); return NULL; } if( el->video_norm != '\0' ) { veejay_msg(VEEJAY_MSG_WARNING,"Norm already set to, ignoring new norm"); } else { el->video_norm = tolower(line[0]); } /* read third line: Number of files */ if( fgets(line, 1024, fd) == NULL ) { veejay_msg(VEEJAY_MSG_DEBUG, "Third line: cannot read number of files"); fclose(fd); vj_el_free(el); return NULL; } if( sscanf(line, "%d", &num_list_files) != 1 ) { veejay_msg(VEEJAY_MSG_DEBUG, "Parse error"); fclose(fd); vj_el_free(el); return NULL; } veejay_msg(VEEJAY_MSG_DEBUG, "Edit list contains %d files", num_list_files); /* read files */ for (i = 0; i < num_list_files; i++) { fgets(line, 1024, fd); n = strlen(line); if (line[n - 1] != '\n') { veejay_msg(VEEJAY_MSG_ERROR, "Filename in edit list too long"); fclose(fd); vj_el_free(el); return NULL; } line[n - 1] = 0; /* Get rid of \n at end */ index_list[i] = open_video_file(line, el, flags, deinterlace,force,norm, av_pixfmt, width, height); if(index_list[i]< 0) { fclose(fd); vj_el_free(el); return NULL; } el->frame_list = (uint64_t *) realloc(el->frame_list, (el->video_frames + el->num_frames[n]) * sizeof(uint64_t)); if (el->frame_list==NULL) { veejay_msg(VEEJAY_MSG_ERROR, "Insufficient memory to allocate frame_list"); fclose(fd); vj_el_free(el); return NULL; } for (i = 0; i < el->num_frames[n]; i++) { el->frame_list[el->video_frames++] = EL_ENTRY(n, i); } } /* Read edit list entries */ while (fgets(line, 1024, fd)) { if (line[0] != ':') { /* ignore lines starting with a : */ sscanf(line, "%ld %d %d", &nl, &n1, &n2); if (nl < 0 || nl >= num_list_files) { veejay_msg(VEEJAY_MSG_ERROR,"Wrong file number in edit list entry"); fclose(fd); vj_el_free(el); return NULL; } if (n1 < 0) n1 = 0; if (n2 >= el->num_frames[index_list[nl]]) n2 = el->num_frames[index_list[nl]]; if (n2 < n1) continue; el->frame_list = (uint64_t *) realloc(el->frame_list, (el->video_frames + n2 - n1 + 1) * sizeof(uint64_t)); if (el->frame_list==NULL) { veejay_msg(VEEJAY_MSG_ERROR, "Insufficient memory to allocate frame_list"); fclose(fd); vj_el_free(el); return NULL; } for (i = n1; i <= n2; i++) { el->frame_list[el->video_frames++] = EL_ENTRY( index_list[ nl], i); } } } /* done reading editlist entries */ fclose(fd); } else { /* Not an edit list - should be a ordinary video file */ fclose(fd); n = open_video_file(filename[nf], el, flags, deinterlace,force,norm, av_pixfmt, width, height); if(n >= 0 ) { el->frame_list = (uint64_t *) realloc(el->frame_list, (RUP8(el->video_frames + el->num_frames[n])) * sizeof(uint64_t)); if (el->frame_list==NULL) { veejay_msg(VEEJAY_MSG_ERROR, "Insufficient memory to allocate frame_list"); vj_el_free(el); return NULL; } for (i = 0; i < el->num_frames[n]; i++) { el->frame_list[el->video_frames++] = EL_ENTRY(n, i); } } } } if( el->num_video_files == 0 || el->video_width == 0 || el->video_height == 0 || el->video_frames < 1) { if( el->video_frames < 1 ) { veejay_msg(VEEJAY_MSG_ERROR, "\tFile has no video frames", el->video_frames ); vj_el_free(el); return NULL; } if( el->num_video_files == 0 ) { veejay_msg(VEEJAY_MSG_ERROR, "\tNo videofiles in EDL"); vj_el_free(el); return NULL; } if( el->video_height == 0 || el->video_width == 0 ) { veejay_msg(VEEJAY_MSG_ERROR, "\tImage dimensions unknown"); vj_el_free(el); return NULL; } vj_el_free(el); return NULL; } long cur_max_frame_size = 0; for ( i = 0; i < el->num_video_files; i ++ ) { if( el->max_frame_sizes[i] > cur_max_frame_size ) cur_max_frame_size = el->max_frame_sizes[i]; } if( cur_max_frame_size == 0 ) { for( i = 0; i < el->num_video_files; i ++ ) { long tmp = get_max_frame_size( el->lav_fd[i] ); if( tmp > cur_max_frame_size ) cur_max_frame_size = tmp; } } el->max_frame_size = cur_max_frame_size; /* Pick a pixel format */ el->pixel_format = el_pixel_format_org; el->total_frames = el->video_frames-1; /* Help for audio positioning */ el->last_afile = -1; veejay_msg(VEEJAY_MSG_DEBUG, "\tThere are %" PRIu64 " video frames", el->total_frames ); el->auto_deinter = 0; return el; } void vj_el_free(editlist *el) { if(!el) return; int i; for ( i = 0; i < el->num_video_files; i ++ ) { if( el->video_file_list[i]) { free(el->video_file_list[i]); el->video_file_list[i] = NULL; } if( el->is_clone ) continue; if( el->ctx[i] ) { avhelper_close_decoder( el->ctx[i] ); } if( el->decoders[i] ) { _el_free_decoder( el->decoders[i] ); } if( el->lav_fd[i] ) { lav_close( el->lav_fd[i] ); el->lav_fd[i] = NULL; } } if( el->cache ) { free_cache( el->cache ); el->cache = NULL; } if( el->frame_list ) { free(el->frame_list ); el->frame_list = NULL; } free(el); el = NULL; } void vj_el_print(editlist *el) { int i; char timecode[64]; char interlacing[64]; MPEG_timecode_t ttc; veejay_msg(VEEJAY_MSG_INFO,"EditList settings: Video:%dx%d@%2.2f %s\tAudio:%d Hz/%d channels/%d bits", el->video_width,el->video_height,el->video_fps,(el->video_norm=='p' ? "PAL" :"NTSC"), el->audio_rate, el->audio_chans, el->audio_bits); for(i=0; i < el->num_video_files ; i++) { long num_frames = lav_video_frames(el->lav_fd[i]); MPEG_timecode_t tc; switch( lav_video_interlacing(el->lav_fd[i])) { case LAV_NOT_INTERLACED: sprintf(interlacing, "Not interlaced"); break; case LAV_INTER_TOP_FIRST: sprintf(interlacing,"Top field first"); break; case LAV_INTER_BOTTOM_FIRST: sprintf(interlacing, "Bottom field first"); break; default: sprintf(interlacing, "Unknown !"); break; } mpeg_timecode(&tc, num_frames, mpeg_framerate_code( mpeg_conform_framerate( el->video_fps )), el->video_fps ); sprintf( timecode, "%2d:%2.2d:%2.2d:%2.2d", tc.h, tc.m, tc.s, tc.f ); veejay_msg(VEEJAY_MSG_INFO, "\tFile %s (%s) with %ld frames (total duration %s)", el->video_file_list[i], interlacing, num_frames, timecode ); } mpeg_timecode(&ttc, el->video_frames, mpeg_framerate_code( mpeg_conform_framerate( el->video_fps )), el->video_fps ); sprintf( timecode, "%2d:%2.2d:%2.2d:%2.2d", ttc.h, ttc.m, ttc.s, ttc.f ); veejay_msg(VEEJAY_MSG_INFO, "\tDuration: %s (%2d hours, %2d minutes)(%ld frames)", timecode,ttc.h,ttc.m,el->video_frames); } MPEG_timecode_t get_timecode(editlist *el, long num_frames) { MPEG_timecode_t tc; veejay_memset(&tc,0,sizeof(tc)); mpeg_timecode(&tc, num_frames, mpeg_framerate_code( mpeg_conform_framerate( el->video_fps )), el->video_fps ); return tc; } int vj_el_get_file_entry(editlist *el, long *start_pos, long *end_pos, long entry ) { if(entry >= el->num_video_files) return 0; int64_t n = (int64_t) entry; int64_t i = 0; if( el->video_file_list[ n ] == NULL ) return 0; *start_pos = 0; for( i = 0;i < n ; i ++ ) *start_pos += el->num_frames[i]; *end_pos = (*start_pos + el->num_frames[n] - 1); return 1; } char *vj_el_write_line_ascii( editlist *el, int *bytes_written ) { if(el == NULL || el->is_empty) return NULL; int num_files = 0; int64_t oldfile, oldframe, of1,ofr; int64_t index[MAX_EDIT_LIST_FILES]; //@ TRACE int64_t n,n1=0; char *result = NULL; int64_t j = 0; int64_t n2 = el->total_frames; char filename[2048]; //@ TRACE char fourcc[6]; //@ TRACE /* get which files are actually referenced in the edit list entries */ int est_len = 0; for (j = 0; j < MAX_EDIT_LIST_FILES; j++) index[j] = -1; for (j = n1; j <= n2; j++) { n = el->frame_list[j]; index[N_EL_FILE(n)] = 1; } num_files = 0; int nnf = 0; long len = 0; for (j = 0; j < MAX_EDIT_LIST_FILES; j++) { if (index[j] >= 0 && el->video_file_list[j] != NULL ) { index[j] = (int64_t)num_files; nnf ++; len += (strlen(el->video_file_list[j])) + 25 + 20; num_files ++; } } n = el->frame_list[n1]; oldfile = index[ N_EL_FILE(n) ]; oldframe = N_EL_FRAME(n); n1 = n; of1 = oldfile; ofr = oldframe; for (j = n1+1; j <= n2; j++) { n = el->frame_list[j]; if ( index[ N_EL_FILE(n) ] != oldfile || N_EL_FRAME(n) != oldframe + 1 ) { len += 64; } oldfile = index[N_EL_FILE(n)]; oldframe = N_EL_FRAME(n); } n = n1; oldfile = of1; oldframe = ofr; n1 = 0; est_len = 2 * len; result = (char*) vj_calloc(sizeof(char) * est_len ); sprintf(result, "%04d",nnf ); for (j = 0; j < MAX_EDIT_LIST_FILES; j++) { if (index[j] >= 0 && el->video_file_list[j] != NULL) { snprintf(fourcc,sizeof(fourcc),"%s", "????"); vj_el_get_file_fourcc( el, j, fourcc ); snprintf(filename,sizeof(filename),"%03zu%s%04lu%010lu%02zu%s", strlen( el->video_file_list[j] ), el->video_file_list[j], (long unsigned int) j, el->num_frames[j], strlen(fourcc), fourcc ); strncat ( result, filename, strlen(filename)); } } char first[128]; char tmpbuf[128]; snprintf(first,sizeof(first), "%016" PRId64 "%016" PRId64 ,oldfile, oldframe); strncat( result, first, strlen(first) ); for (j = n1+1; j <= n2; j++) { n = el->frame_list[j]; if ( index[ N_EL_FILE(n) ] != oldfile || N_EL_FRAME(n) != oldframe + 1 ) { snprintf( tmpbuf,sizeof(tmpbuf), "%016" PRId64 "%016" PRId64 "%016llu", oldframe, index[N_EL_FILE(n)], N_EL_FRAME(n) ); strncat( result, tmpbuf, strlen(tmpbuf) ); } oldfile = index[N_EL_FILE(n)]; oldframe = N_EL_FRAME(n); } char last_word[64]; snprintf(last_word,sizeof(last_word),"%016" PRId64, oldframe); strncat( result, last_word, strlen(last_word) ); int datalen = strlen(result); *bytes_written = datalen; return result; } int vj_el_write_editlist( char *name, long _n1, long _n2, editlist *el ) { FILE *fd; int num_files; int64_t oldfile, oldframe; int64_t index[MAX_EDIT_LIST_FILES]; int64_t n; int64_t n1 = (uint64_t) _n1; int64_t n2 = (uint64_t) _n2; int64_t i; if(!el) return 0; if(el->is_empty) { veejay_msg(VEEJAY_MSG_ERROR, "No frames in EditList" ); return 0; } if (n1 < 0) n1 = 0; if (n2 > el->total_frames || n2 < n1) n2 = el->total_frames; fd = fopen(name, "w"); if (!fd) { veejay_msg(VEEJAY_MSG_ERROR,"Can not open %s - no edit list written!", name); return 0; } fprintf(fd, "LAV Edit List\n"); fprintf(fd, "%s\n", el->video_norm == 'n' ? "NTSC" : "PAL"); /* get which files are actually referenced in the edit list entries */ for (i = 0; i < MAX_EDIT_LIST_FILES; i++) index[i] = -1; for (i = n1; i <= n2; i++) index[N_EL_FILE(el->frame_list[i])] = 1; num_files = 0; for (i = 0; i < MAX_EDIT_LIST_FILES; i++) if (index[i] > 0) index[i] = (int64_t)num_files++; fprintf(fd, "%d\n", num_files); for (i = 0; i < MAX_EDIT_LIST_FILES; i++) if (index[i] >= 0 && el->video_file_list[i] != NULL) { fprintf(fd, "%s\n", el->video_file_list[i]); } n = el->frame_list[ n1 ]; oldfile = index[N_EL_FILE(n)]; oldframe = N_EL_FRAME(n); fprintf(fd, "%" PRId64 " %" PRId64 " ", oldfile, oldframe); for (i = n1 + 1; i <= n2; i++) { n = el->frame_list[i]; if (index[N_EL_FILE(n)] != oldfile || N_EL_FRAME(n) != oldframe + 1) { fprintf(fd, "%" PRId64 "\n", oldframe); fprintf(fd, "%" PRId64 " %llu ",index[N_EL_FILE(n)], N_EL_FRAME(n)); } oldfile = index[N_EL_FILE(n)]; oldframe = N_EL_FRAME(n); } n = fprintf(fd, "%" PRId64 "\n", oldframe); /* We did not check if all our prints succeeded, so check at least the last one */ if (n <= 0) { veejay_msg(VEEJAY_MSG_ERROR,"Error writing edit list: "); return 0; } fclose(fd); return 1; } editlist *vj_el_soft_clone(editlist *el) { editlist *clone = (editlist*) vj_calloc(sizeof(editlist)); if(!clone) return NULL; clone->is_empty = el->is_empty; clone->has_video = el->has_video; clone->video_width = el->video_width; clone->video_height = el->video_height; clone->video_fps = el->video_fps; clone->video_sar_width = el->video_sar_width; clone->video_sar_height = el->video_sar_height; clone->video_norm = el->video_norm; clone->has_audio = el->has_audio; clone->audio_rate = el->audio_rate; clone->audio_chans = el->audio_chans; clone->audio_bits = el->audio_bits; clone->audio_bps = el->audio_bps; clone->video_frames = el->video_frames; clone->total_frames = el->video_frames - 1; clone->num_video_files = el->num_video_files; clone->max_frame_size = el->max_frame_size; clone->MJPG_chroma = el->MJPG_chroma; clone->frame_list = NULL; clone->last_afile = el->last_afile; clone->last_apos = el->last_apos; clone->auto_deinter = el->auto_deinter; clone->pixel_format = el->pixel_format; clone->is_clone = 1; int i; for( i = 0; i < MAX_EDIT_LIST_FILES; i ++ ) { clone->video_file_list[i] = NULL; clone->lav_fd[i] = NULL; clone->num_frames[i] = 0; clone->pixfmt[i] = 0; if( el->lav_fd[i] && el->video_file_list[i]) { clone->video_file_list[i] = vj_strdup( el->video_file_list[i] ); clone->lav_fd[i] = el->lav_fd[i]; clone->num_frames[i] = el->num_frames[i]; clone->pixfmt[i] =el->pixfmt[i]; } clone->decoders[i] = el->decoders[i]; clone->ctx[i] = el->ctx[i]; } return clone; } int vj_el_framelist_clone( editlist *src, editlist *dst) { if(!src || !dst) return 0; if(dst->frame_list) return 0; dst->frame_list = (uint64_t*) vj_malloc(sizeof(uint64_t) * RUP8(src->video_frames) ); if(!dst->frame_list) return 0; veejay_memcpy( dst->frame_list, src->frame_list, (sizeof(uint64_t) * src->video_frames ) ); return 1; } editlist *vj_el_clone(editlist *el) { editlist *clone = (editlist*) vj_el_soft_clone(el); if(!clone) return NULL; if( vj_el_framelist_clone( el, clone ) ) return clone; else { if(clone) vj_el_free(clone); veejay_msg(VEEJAY_MSG_ERROR, "Cannot clone: Memory error?!"); } return clone; }