videobuffer.c

来自「从 IEEE 1394总线接收传输流」· C语言 代码 · 共 362 行

C
362
字号
/* * MPEG2-TS over IEEE 1394 decoder - receive and decode MPEG-2 transport *                                   streams according to IEC 61883-4 * * Copyright (C) 2000-2007, Manfred Weihs <mweihs@users.sourceforge.net> * * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */#include <stdlib.h>#include <stdio.h>#include <string.h>#include "videobuffer.h"#include <inttypes.h>#include <pthread.h>#include <time.h>#include <mpeg2.h>#include "video_out.h"#include "synchronisation.h"#include "config.h"#ifdef VIDEO_FIX_WINDOWextern void clear_tv_window();#endifextern int debug;struct videobuffer_entry videobuffer[VIDEOPESPACKETSINBUFFER];volatile int videobufferstart; /* points to the entry which the consumer currently consumes */volatile int videobufferend;   /* points to the entry which the producer currently fills */sem_t videobuffer_sem;static pthread_t video_decoder_thread;static volatile int terminate_decoder;static int havedts;static unsigned long long int dts;static mpeg2dec_t * mpeg2dec;static vo_instance_t * output;static void process_picture_startcode() {	if (havedts && is_sync_enabled())		fprintf(stderr,"warning: getting new video dts before using the last one!\n");	dts=videobuffer[videobufferstart].dts;	havedts=videobuffer[videobufferstart].havedts;	videobuffer[videobufferstart].havedts=0;}static int wait_for_pts() {	struct timespec req;	if (is_sync_enabled())	{		if (havedts)		{			long long stc = getSTC();			long long int deviation=(long long int) dts - stc; /* how long shall we delay this frame */			/* compensate overflow in pcr or dts */			/* bring difference into the interval [-4294967296;4294967295] */			if (deviation < -4294967296ll) /* is in [-8589934591;-4294967297] */				deviation +=8589934592ll;			else if (deviation > 4294967295ll) /* is in [4294967296;8589934591] */				deviation -= 8589934592ll;			havedts=0;			if (deviation < (-90000/25)) { /* we are more than 1/25 s late */				fprintf(stderr,"Video frame dropped, because it is too late\n");				return 0;			}			if (deviation > (90000*3)) { /* more than 3 second too early */				fprintf(stderr,"Video frame more than 3 seconds too early, it seems we lost synchronisation.\n");				return 1;			}			if (deviation < 0) {				if (debug) {					fprintf(stdout,"Video needs no sleep, because we are already a %lld ms late\n", deviation/90);				}				return 1;			}			req.tv_sec = deviation / 90000;			req.tv_nsec = (deviation % 90000) * 100000 / 9;			if (nanosleep(&req, NULL)) {				fprintf(stderr,"sleep before displaying video frame was too short\n");			}			return 1;		}		else {			fprintf(stderr,"warning: we have no pts for this video frame -> displaying it immediately.\n");		}	}	return 1;}static void* video_decoder_thread_function(void *arg){	while (1)	{		if (sem_wait(&videobuffer_sem))		{			fprintf(stderr,"Cannot P semaphore\n");			exit(0);		}		if (terminate_decoder)		{			return NULL; /* I think, that is cleaner than pthread_exit(0) */		}		if ((videobufferstart +1) % VIDEOPESPACKETSINBUFFER  == videobufferend) /* this may not happen because of semaphore */		{			fprintf(stderr,"no frame in video buffer, stopped\n");			exit(0);		}		else		{			int old_havedts=videobuffer[videobufferstart].havedts;  /* store value of dts of the old packet */			unsigned long long int old_dts=videobuffer[videobufferstart].dts;			const mpeg2_info_t * info;			int state;			vo_setup_result_t setup_result;			videobufferstart = (videobufferstart+1) % VIDEOPESPACKETSINBUFFER; /* advance buffer */			if (videobuffer[videobufferstart].continuation) /* if this is not a new PES packet */			{  /* continue using old value of dts */				videobuffer[videobufferstart].dts=old_dts;				videobuffer[videobufferstart].havedts=old_havedts;			}			else			{				if (old_havedts)					fprintf(stderr,"warning: DTS of video packet dropped unused\n");			}			mpeg2_buffer(mpeg2dec, videobuffer[videobufferstart].buffer,			             videobuffer[videobufferstart].buffer+videobuffer[videobufferstart].length);			info = mpeg2_info(mpeg2dec);			do {				state = mpeg2_parse (mpeg2dec);				switch (state) {				case STATE_BUFFER:					break;				case STATE_SEQUENCE:					/* might set nb fbuf, convert format, stride */					/* might set fbufs */					if (output->setup (output, info->sequence->width,							   info->sequence->height,					                   info->sequence->chroma_width,					                   info->sequence->chroma_height, &setup_result)) {						fprintf (stderr, "display setup failed\n");						exit (1);					}					if (setup_result.convert &&					    mpeg2_convert (mpeg2dec, setup_result.convert, NULL)) {						fprintf (stderr, "color conversion setup failed\n");						exit (1);					}					if (output->set_fbuf) {						uint8_t * buf[3];						void * id;						mpeg2_custom_fbuf (mpeg2dec, 1);						output->set_fbuf (output, buf, &id);						mpeg2_set_buf (mpeg2dec, buf, id);						output->set_fbuf (output, buf, &id);						mpeg2_set_buf (mpeg2dec, buf, id);					} else if (output->setup_fbuf) {						uint8_t * buf[3];						void * id;						output->setup_fbuf (output, buf, &id);						mpeg2_set_buf (mpeg2dec, buf, id);						output->setup_fbuf (output, buf, &id);						mpeg2_set_buf (mpeg2dec, buf, id);						output->setup_fbuf (output, buf, &id);						mpeg2_set_buf (mpeg2dec, buf, id);					}					break;				case STATE_PICTURE:					process_picture_startcode(); // I am not sure, whether that is really correct here...					//MAYBE call mpeg2_skip to skip, if we are already late here.					/* might skip */					/* might set fbuf */					if (output->set_fbuf) {						uint8_t * buf[3];						void * id;						output->set_fbuf (output, buf, &id);						mpeg2_set_buf (mpeg2dec, buf, id);					}					if (output->start_fbuf) {						if (info->current_fbuf) {							output->start_fbuf (output, info->current_fbuf->buf,							                    info->current_fbuf->id);						} else {							fprintf(stderr,"warning: current_fbuf is NULL, maybe bug in libmpeg2\n");						}					}					break;				case STATE_SLICE:				case STATE_END:				case STATE_INVALID_END:					if (wait_for_pts()) {						/* draw current picture */						if (info->display_fbuf) {							if (output->draw)								output->draw (output, info->display_fbuf->buf,								              info->display_fbuf->id);						}					}					/* might free frame buffer */					if (output->discard && info->discard_fbuf)						output->discard (output, info->discard_fbuf->buf,						                 info->discard_fbuf->id);					break;				}			} while (state != STATE_BUFFER);			if (videobuffer[videobufferstart].flags & VIDEOBUFFER_FLAGS_CLEARSCREEN) { /* we shall clear the screen */				//FIXME: For now we only clear the fix window. It would, however, be better				//to add a clearscreen method to vo_instance_t#ifdef VIDEO_FIX_WINDOW				clear_tv_window();#endif			}		}	}	return 0; /* will never be reached */}#ifdef VIDEO_FIX_WINDOWstatic char vo_driver[100] = "xv fix";#elsestatic char vo_driver[100] = "xv";#endifvoid print_vo_drivers() {	int i;	vo_driver_t const * drivers;	drivers = vo_drivers();	fprintf(stdout, "available video out drivers:\n");	for (i = 0; drivers[i].name; i++) {		fprintf(stdout, "     %s\n", drivers[i].name);	}	fprintf(stdout, "currently selected video out driver: %s\n", vo_driver);}void set_vo_driver(const char* driver_name) {	strncpy(vo_driver, driver_name, sizeof(vo_driver));}int video_system_init(){	vo_open_t * output_open = NULL;	vo_driver_t const * drivers;	int i;		drivers = vo_drivers ();	for (i = 0; drivers[i].name != NULL; i++) {		if (strcmp (drivers[i].name, vo_driver) == 0) {			output_open = drivers[i].open;		}	}	if (output_open == NULL) {		fprintf (stderr, "video out driver %s not found\n", vo_driver);		if (drivers[0].name != NULL) {			fprintf (stderr, "using driver %s instead\n", drivers[0].name);			output_open = drivers[0].open;		}	}	if (output_open == NULL) {		fprintf(stderr,"no valid output driver found\n");		exit(1);	}	output = output_open();	if (output == NULL) {		fprintf(stderr,"Cannot open output\n");		exit(1);	}	mpeg2dec = mpeg2_init();	videobufferstart=0;	videobufferend=1;	havedts=0;	videobuffer[videobufferend].length=0;	videobuffer[videobufferend].havedts=0;	videobuffer[videobufferend].flags=0;	terminate_decoder=0;	if (sem_init(&videobuffer_sem,0,0))	{		fprintf(stderr,"Cannot create video semaphore\n");		exit(1);	}	if (pthread_create(&video_decoder_thread,NULL,video_decoder_thread_function,0))	{		fprintf(stderr,"Cannot create video decoder thread\n");		exit(1);	}	return 1;}int video_system_done(){	void *result;	terminate_decoder=1;	if (sem_post(&videobuffer_sem))  /* we do this to ensure that the thread is activated */	{		fprintf(stderr,"Cannot V semaphore\n");		exit(0);	}	if (pthread_join(video_decoder_thread,&result))	/* wait for thread to exit */	{		fprintf(stderr,"Cannot join video decoder thread\n");		exit(1);	}	mpeg2_close(mpeg2dec);	if (output->close) {		output->close(output);	}	fprintf(stdout,"Video decoder terminated\n");fflush(stdout);	if (sem_destroy(&videobuffer_sem))	{		fprintf(stderr,"Cannot destroy video semaphore\n");		exit(1);	}	return 1;}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?