/* * Linux VeeJay * * Copyright(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 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int lock(vj_yuv *t) { return pthread_mutex_lock( &(t->mutex )); } static int unlock(vj_yuv *t) { return pthread_mutex_unlock( &(t->mutex )); } #define RUP8(num)(((num)+8)&~8) vj_yuv *vj_yuv4mpeg_alloc(int w, int h, float fps, int out_pixel_format) { vj_yuv *yuv4mpeg = (vj_yuv *) vj_calloc(sizeof(vj_yuv)); if(!yuv4mpeg) return NULL; yuv4mpeg->sar = y4m_sar_UNKNOWN; yuv4mpeg->dar = y4m_dar_4_3; y4m_accept_extensions( 1 ); y4m_init_stream_info(&(yuv4mpeg->streaminfo)); y4m_init_frame_info(&(yuv4mpeg->frameinfo)); yuv4mpeg->width = w; yuv4mpeg->format = vj_to_pixfmt(out_pixel_format); yuv4mpeg->height = h; yuv4mpeg->video_fps = fps; yuv4mpeg->chroma = get_chroma_from_pixfmt( yuv4mpeg->format ); yuv4mpeg->scaler = NULL; return yuv4mpeg; } void vj_yuv4mpeg_free(vj_yuv *v) { if(v) { if( v->scaler ) { yuv_free_swscaler( v->scaler ); v->scaler = NULL; } if(v->buf[0] != NULL ) free(v->buf[0]); if(v->src) free(v->src); if(v->dst) free(v->dst); free(v); } } static int vj_yuv_stream_start_read1(vj_yuv * yuv4mpeg, int fd, int width, int height); int vj_yuv_stream_start_read(vj_yuv * yuv4mpeg, char *filename, int width,int height) { int fd = open(filename,O_RDONLY); if(!fd) { veejay_msg(VEEJAY_MSG_ERROR, "Unable to open video stream %s\n", filename); return -1; } return vj_yuv_stream_start_read1( yuv4mpeg, fd, width,height ); } int vj_yuv_stream_start_read_fd( vj_yuv *yuv4mpeg, int fd, int width,int height ) { return vj_yuv_stream_start_read1( yuv4mpeg,fd,width,height ); } static int vj_yuv_stream_start_read1(vj_yuv * yuv4mpeg, int fd, int width, int height) { int i, w, h; yuv4mpeg->fd = fd; i = y4m_read_stream_header(yuv4mpeg->fd, &(yuv4mpeg->streaminfo)); if (i != Y4M_OK) { veejay_msg(VEEJAY_MSG_ERROR, "yuv4mpeg: %s", y4m_strerr(i)); return -1; } w = y4m_si_get_width(&(yuv4mpeg->streaminfo)); h = y4m_si_get_height(&(yuv4mpeg->streaminfo)); yuv4mpeg->chroma = y4m_si_get_chroma( &(yuv4mpeg->streaminfo) ); yuv4mpeg->buf[0] = vj_calloc( sizeof(uint8_t) * RUP8( w * h * 4) ); yuv4mpeg->buf[1] = yuv4mpeg->buf[0] + (w*h); yuv4mpeg->buf[2] = yuv4mpeg->buf[1] + (w*h); yuv4mpeg->buf[3] = yuv4mpeg->buf[2] + (w*h); int plane_count = y4m_si_get_plane_count( &(yuv4mpeg->streaminfo) ); int src_fmt = get_pixfmt_from_chroma( yuv4mpeg->chroma ); veejay_msg(VEEJAY_MSG_DEBUG, "Frame: %dx%d, number of planes: %d, chroma: %d",w,h,plane_count, y4m_chroma_keyword( yuv4mpeg->chroma )); for( i =0; i < plane_count ; i ++ ) { yuv4mpeg->plane_w[i] = y4m_si_get_plane_width( &(yuv4mpeg->streaminfo), i); yuv4mpeg->plane_h[i] = y4m_si_get_plane_height( &(yuv4mpeg->streaminfo), i); yuv4mpeg->plane_size[i] = y4m_si_get_plane_length( &(yuv4mpeg->streaminfo), i ); } yuv4mpeg->src = yuv_yuv_template( yuv4mpeg->buf[0], yuv4mpeg->buf[1], yuv4mpeg->buf[2],w,h, src_fmt ); yuv4mpeg->dst = yuv_yuv_template( NULL,NULL,NULL, yuv4mpeg->width,yuv4mpeg->height, yuv4mpeg->format ); sws_template sws_tem; veejay_memset(&sws_tem, 0,sizeof(sws_template)); sws_tem.flags = yuv_which_scaler(); yuv4mpeg->scaler = yuv_init_swscaler( yuv4mpeg->src,yuv4mpeg->dst, &sws_tem, yuv_sws_get_cpu_flags()); if(!yuv4mpeg->scaler) { veejay_msg(VEEJAY_MSG_ERROR, "yuv4mpeg: unable to initialize scaler context" ); return -1; } return 0; } int vj_yuv_stream_write_header(vj_yuv * yuv4mpeg, VJFrame *frame, int out_chroma) { int i = 0; y4m_si_set_width(&(yuv4mpeg->streaminfo), yuv4mpeg->width); y4m_si_set_height(&(yuv4mpeg->streaminfo), yuv4mpeg->height); y4m_si_set_interlace(&(yuv4mpeg->streaminfo), Y4M_ILACE_NONE); y4m_si_set_chroma( &(yuv4mpeg->streaminfo), out_chroma ); y4m_si_set_framerate(&(yuv4mpeg->streaminfo), mpeg_conform_framerate(frame->fps)); yuv4mpeg->chroma = out_chroma; yuv4mpeg->buf[0] = vj_calloc(sizeof(uint8_t) * yuv4mpeg->width * yuv4mpeg->height * 4 ); yuv4mpeg->buf[1] = yuv4mpeg->buf[0] + yuv4mpeg->width * yuv4mpeg->height; yuv4mpeg->buf[2] = yuv4mpeg->buf[1] + yuv4mpeg->width * yuv4mpeg->height; yuv4mpeg->buf[3] = yuv4mpeg->buf[2] + yuv4mpeg->width * yuv4mpeg->height; y4m_ratio_t dar2 = y4m_guess_sar(yuv4mpeg->width,yuv4mpeg->height,yuv4mpeg->dar); y4m_si_set_sampleaspect(&(yuv4mpeg->streaminfo), dar2); i = y4m_write_stream_header(yuv4mpeg->fd, &(yuv4mpeg->streaminfo)); y4m_log_stream_info(2, "yuv4mpeg: ", &(yuv4mpeg->streaminfo)); if (i != Y4M_OK) return -1; sws_template sws_tem; veejay_memset(&sws_tem, 0,sizeof(sws_template)); sws_tem.flags = yuv_which_scaler(); yuv4mpeg->dst = yuv_yuv_template( yuv4mpeg->buf[0],yuv4mpeg->buf[1],yuv4mpeg->buf[2], frame->width,frame->height, get_pixfmt_from_chroma(out_chroma)); yuv4mpeg->src = yuv_yuv_template( NULL,NULL,NULL, yuv4mpeg->width,yuv4mpeg->height, yuv4mpeg->format ); yuv4mpeg->scaler = yuv_init_swscaler( yuv4mpeg->src, yuv4mpeg->dst, &sws_tem, yuv_sws_get_cpu_flags()); return 0; } int vj_yuv_stream_open_pipe(vj_yuv *yuv4mpeg, char *filename) { yuv4mpeg->fd = open(filename,O_WRONLY,0600); if(!yuv4mpeg->fd) return 0; return 1; } int vj_yuv_stream_header_pipe(vj_yuv *yuv4mpeg,VJFrame *frame) { if( vj_yuv_stream_write_header(yuv4mpeg, frame, get_chroma_from_pixfmt(frame->format) ) == 0 ) return 1; return 0; } static void *y4m_writer_thread( void *data ) { vj_yuv *y4m = (vj_yuv*)data; lock(y4m); y4m->state = 1; unlock(y4m); for( ;; ) { lock(y4m); if( y4m->state == 0 ) { unlock(y4m); goto Y4MTHREADEXIT; } pthread_cond_wait( &(y4m->task), &(y4m->mutex)); unlock(y4m); if( y4m->state == 0 ) { goto Y4MTHREADEXIT; } int error = y4m_write_frame( y4m->fd, &(y4m->streaminfo), &(y4m->frameinfo), y4m->dst->data); if (error != Y4M_OK) { veejay_msg(VEEJAY_MSG_ERROR, "yuv4mpeg: %s", y4m_strerr(error)); goto Y4MTHREADEXIT; } if( y4m->state == 0 ) { goto Y4MTHREADEXIT; } } Y4MTHREADEXIT: veejay_msg(VEEJAY_MSG_INFO,"Exiting Y4M writer thread" ); return NULL; } int vj_yuv_stream_start_write(vj_yuv * yuv4mpeg,VJFrame *frame, char *filename, int outchroma) { //if(mkfifo( filename, 0600)!=0) return -1; /* if the file exists gamble and simply append, if it does not exist write header */ struct stat sstat; if(strncasecmp( filename, "stdout", 6) == 0) { yuv4mpeg->fd = 1; } else { if(strncasecmp(filename, "stderr", 6) == 0) { yuv4mpeg->fd = 2; } else { if (stat(filename, &sstat) == 0) { if (S_ISREG(sstat.st_mode)) { /* the file is a regular file */ yuv4mpeg->fd = open(filename, O_APPEND | O_WRONLY, 0600); if (!yuv4mpeg->fd) return -1; } else { if (S_ISFIFO(sstat.st_mode)) veejay_msg(VEEJAY_MSG_INFO, "Waiting for a program to open %s", filename); yuv4mpeg->fd = open(filename,O_WRONLY,0600); if(!yuv4mpeg->fd) return 0; } } else { veejay_msg(VEEJAY_MSG_INFO, "Creating YUV4MPEG regular file '%s'",filename); yuv4mpeg->fd = open(filename, O_CREAT | O_WRONLY, 0600); if (!yuv4mpeg->fd) return -1; } } } if( vj_yuv_stream_write_header(yuv4mpeg, frame, outchroma) < 0 ) { veejay_msg(VEEJAY_MSG_ERROR, "Error while writing y4m header."); return -1; } yuv4mpeg->has_audio = 0; pthread_mutex_init( &(yuv4mpeg->mutex), NULL ); int p_err = pthread_create( &(yuv4mpeg->thread), NULL, y4m_writer_thread, (void*) yuv4mpeg ); if( p_err == 0 ) { veejay_msg(VEEJAY_MSG_INFO, "Created new Y4M writer thread" ); } return 0; } void vj_yuv_stream_stop_write(vj_yuv * yuv4mpeg) { y4m_fini_stream_info(&(yuv4mpeg->streaminfo)); y4m_fini_frame_info(&(yuv4mpeg->frameinfo)); close(yuv4mpeg->fd); if( yuv4mpeg->scaler ) { yuv_free_swscaler( yuv4mpeg->scaler ); yuv4mpeg->scaler = NULL; } lock( yuv4mpeg ); if( yuv4mpeg->state == 0 ) { unlock(yuv4mpeg); return; } yuv4mpeg->state = 0; unlock(yuv4mpeg); if( yuv4mpeg->src ) { free( yuv4mpeg->src ); yuv4mpeg->src = NULL; } if( yuv4mpeg->dst ) { free( yuv4mpeg->dst ); yuv4mpeg->dst = NULL; } pthread_mutex_destroy(&(yuv4mpeg->mutex)); pthread_cond_destroy(&(yuv4mpeg->task)); } void vj_yuv_stream_stop_read(vj_yuv * yuv4mpeg) { y4m_fini_stream_info(&(yuv4mpeg->streaminfo)); y4m_fini_frame_info(&(yuv4mpeg->frameinfo)); close(yuv4mpeg->fd); yuv4mpeg->sar = y4m_sar_UNKNOWN; yuv4mpeg->dar = y4m_dar_4_3; if( yuv4mpeg->scaler ) { yuv_free_swscaler( yuv4mpeg->scaler ); yuv4mpeg->scaler = NULL; } if( yuv4mpeg->buf[0] ) { free(yuv4mpeg->buf[0]); yuv4mpeg->buf[0] = NULL; } if( yuv4mpeg->src ) { free( yuv4mpeg->src ); yuv4mpeg->src = NULL; } if( yuv4mpeg->dst ) { free( yuv4mpeg->dst ); yuv4mpeg->dst = NULL; } } static int vj_yuv_restart(vj_yuv *yuv4mpeg ) { y4m_stream_info_t dummy; if( lseek( yuv4mpeg->fd , 0, SEEK_SET ) < 0 ) { veejay_msg(VEEJAY_MSG_ERROR, "Error seeking to start of y4m stream."); return -1; } //@ read out header int i = y4m_read_stream_header(yuv4mpeg->fd, &dummy); if (i != Y4M_OK) { veejay_msg(VEEJAY_MSG_ERROR, "yuv4mpeg: %s", y4m_strerr(i)); return -1; } return 0; } int vj_yuv_get_frame(vj_yuv * yuv4mpeg, uint8_t *dst[3]) { int i = y4m_read_frame(yuv4mpeg->fd, &(yuv4mpeg->streaminfo),&(yuv4mpeg->frameinfo), yuv4mpeg->buf); if( i != Y4M_OK ) { if( i == Y4M_ERR_EOF ) { if( vj_yuv_restart( yuv4mpeg ) == 0 ) { i = y4m_read_frame(yuv4mpeg->fd,&(yuv4mpeg->streaminfo),&(yuv4mpeg->frameinfo),yuv4mpeg->buf); if( i != Y4M_OK ) { veejay_msg(VEEJAY_MSG_ERROR, "yuv4mpeg %s", y4m_strerr(i)); return -1; } } } else { veejay_msg(VEEJAY_MSG_ERROR, "yuv4mpeg %s", y4m_strerr(i)); return -1; } } yuv4mpeg->dst->data[0] = dst[0]; yuv4mpeg->dst->data[1] = dst[1]; yuv4mpeg->dst->data[2] = dst[2]; yuv_convert_and_scale( yuv4mpeg->scaler, yuv4mpeg->src, yuv4mpeg->dst ); return 1; } int vj_yuv_put_frame(vj_yuv * vjyuv, uint8_t **src) { if (!vjyuv->fd) { veejay_msg(VEEJAY_MSG_ERROR, "Invalid file descriptor for y4m stream"); return -1; } vjyuv->src->data[0] = src[0]; vjyuv->src->data[1] = src[1]; vjyuv->src->data[2] = src[2]; yuv_convert_and_scale( vjyuv->scaler, vjyuv->src, vjyuv->dst ); lock(vjyuv); pthread_cond_signal( &(vjyuv->task) ); unlock(vjyuv); return 0; }