📄 video_output.c
字号:
/* Ogle - A video player * Copyright (C) 2000, 2001 Bj鰎n Englund, H錵an Hjort * * 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 <stdio.h>#include <stdlib.h>#include <unistd.h>#include <signal.h>#include <sys/shm.h>#include <time.h>#include <errno.h>#ifdef __bsdi__#include <sys/param.h>#include <sys/sysctl.h>#endif#ifndef HAVE_CLOCK_GETTIME#include <sys/time.h>#endif#include <X11/Xlib.h>#include <ogle/msgevents.h>#include "debug_print.h"#include "common.h"#include "video_types.h"#include "timemath.h"#include "sync.h"#include "spu_mixer.h"#include "video_output.h"#include "xscreensaver-comm.h"#include "screenshot.h"#ifndef SHM_SHARE_MMU#define SHM_SHARE_MMU 0#endifextern void display_init(int padded_width, int padded_height, int picture_buffer_shmid, char *picture_buffer_addr);extern void display_reset(void);extern void display(yuv_image_t *current_image);extern void display_poll(yuv_image_t *current_image);extern void display_exit(void);extern void display_reset_screensaver(void);extern void screenshot_mode(int mode);int register_event_handler(int(*eh)(MsgEventQ_t *, MsgEvent_t *));int event_handler(MsgEventQ_t *q, MsgEvent_t *ev);char *program_name;int dlevel;int video_scr_nr;int process_interrupted = 0;Bool talk_to_xscreensaver;static int end_of_wait;static long clk_tck;static long min_time_left;AspectModeSrc_t aspect_mode = AspectModeSrcVM;AspectModeSrc_t aspect_sender;uint16_t aspect_new_frac_n;uint16_t aspect_new_frac_d;ZoomMode_t zoom_mode = ZoomModeResizeAllowed;static int ctrl_data_shmid;ctrl_data_t *ctrl_data;ctrl_time_t *ctrl_time;int msgqid = -1;MsgEventQ_t *msgq;static int flush_to_scrid = -1;static int prev_scr_nr = 0;static data_q_t *data_q_head;data_q_t *cur_data_q;static MsgEventClient_t gui_client = 0;MsgEventClient_t input_client = 0;InputMask_t input_mask = INPUT_MASK_None;typedef struct _event_handler_t { int(*eh)(MsgEventQ_t *, MsgEvent_t *); struct _event_handler_t *next;} event_handler_t;static event_handler_t *eh_head = NULL;int register_event_handler(int(*eh)(MsgEventQ_t *, MsgEvent_t *)){ event_handler_t *eh_ptr; eh_ptr = malloc(sizeof(event_handler_t)); eh_ptr->next = eh_head; eh_ptr->eh = eh; eh_head = eh_ptr; return 0;}int event_handler(MsgEventQ_t *q, MsgEvent_t *ev){ event_handler_t *eh_ptr; eh_ptr = eh_head; while(eh_ptr != NULL) { if(eh_ptr->eh(q, ev)) { return 1; } eh_ptr = eh_ptr->next; } DNOTE("event_handler: unhandled event: %d\n", ev->type); return 0;}void wait_for_q_attach(void){ MsgEvent_t ev; //DNOTE("waiting for attachq\n"); while(ev.type != MsgEventQAttachQ) { if(MsgNextEventInterruptible(msgq, &ev) == -1) { switch(errno) { case EINTR: continue; break; default: FATAL("%s", "waiting for attachq\n"); perror("MsgNextEvent"); exit(1); break; } } event_handler(msgq, &ev); } //DNOTE("got attachq\n");}static int attach_ctrl_shm(int shmid){ char *shmaddr; if(ctrl_data_shmid) { return 0; } if(shmid >= 0) { if((shmaddr = shmat(shmid, NULL, SHM_SHARE_MMU)) == (void *)-1) { ERROR("%s", "attach_ctrl_data()\n"); perror("shmat"); return -1; } ctrl_data_shmid = shmid; ctrl_data = (ctrl_data_t*)shmaddr; ctrl_time = (ctrl_time_t *)(shmaddr+sizeof(ctrl_data_t)); } return 0;}static int attach_data_q(int q_shmid, data_q_t *data_q){ int n; int data_shmid; char *q_shmaddr; char *data_shmaddr; q_head_t *q_head; q_elem_t *q_elems; data_buf_head_t *data_head; picture_data_elem_t *data_elems; yuv_image_t *image_bufs;#ifdef HAVE_XV yuv_image_t *reserv_image;#endif //DNOTE("attach_data_q: q_shmid: %d\n", q_shmid); if(q_shmid < 0) { ERROR("%s", "attach_data_q(), q_shmid < 0\n"); return -1; } if((q_shmaddr = shmat(q_shmid, NULL, SHM_SHARE_MMU)) == (void *)-1) { ERROR("%s", "attach_data_q()\n"); perror("shmat"); return -1; } q_head = (q_head_t *)q_shmaddr; q_elems = (q_elem_t *)(q_shmaddr+sizeof(q_head_t)); data_shmid = q_head->data_buf_shmid; //DNOTE("attach_data_q: data_shmid: %d\n", data_shmid); if(data_shmid < 0) { ERROR("%s", "attach_data_q(), data_shmid\n"); return -1; } if((data_shmaddr = shmat(data_shmid, NULL, SHM_SHARE_MMU)) == (void *)-1) { ERROR("%s", "attach_data_q()"); perror("shmat"); return -1; } data_head = (data_buf_head_t *)data_shmaddr; data_elems = (picture_data_elem_t *)(data_shmaddr + sizeof(data_buf_head_t)); //TODO ugly hack#ifdef HAVE_XV image_bufs = malloc((data_head->nr_of_dataelems+1) * sizeof(yuv_image_t)); for(n = 0; n < (data_head->nr_of_dataelems+1); n++) {#else image_bufs = malloc(data_head->nr_of_dataelems * sizeof(yuv_image_t)); for(n = 0; n < data_head->nr_of_dataelems; n++) {#endif image_bufs[n].y = data_shmaddr + data_elems[n].picture.y_offset; image_bufs[n].u = data_shmaddr + data_elems[n].picture.u_offset; image_bufs[n].v = data_shmaddr + data_elems[n].picture.v_offset; image_bufs[n].info = &data_elems[n]; } //TODO ugly hack#ifdef HAVE_XV reserv_image = &image_bufs[n-1];#endif if(data_head->info.type != DataBufferType_Video) { ERROR("%s", "Didn't get type Video\n"); return -1; } data_q->in_use = 0; data_q->eoq = 0; data_q->q_head = q_head; data_q->q_elems = q_elems; data_q->data_head = data_head; data_q->data_elems = data_elems; data_q->image_bufs = image_bufs;#ifdef HAVE_XV data_q->reserv_image = reserv_image;#endif data_q->next = NULL; return 0;}static int detach_data_q(int q_shmid, data_q_t **data_q_head){ MsgEvent_t ev; MsgEventClient_t client; data_q_t **data_q_p; data_q_t *data_q_tmp; //DNOTE("detach_data_q q_shmid: %d\n", q_shmid); for(data_q_p=data_q_head; *data_q_p != NULL && (*data_q_p)->q_head->qid != q_shmid; data_q_p = &(*data_q_p)->next) { } if(*data_q_p == NULL) { ERROR("%s", "detach_data_q q_shmid not found\n"); return -1; } client = (*data_q_p)->q_head->writer; if(shmdt((char *)(*data_q_p)->data_head) == -1) { ERROR("%s", "detach_data_q data_head"); perror("shmdt"); } if(shmdt((char *)(*data_q_p)->q_head) == -1) { ERROR("%s", "detach_data_q q_head"); perror("shmdt"); } //TODO ugly hack free((*data_q_p)->image_bufs); data_q_tmp = *data_q_p; *data_q_p = (*data_q_p)->next; free(data_q_tmp); ev.type = MsgEventQQDetached; ev.detachq.q_shmid = q_shmid; if(MsgSendEvent(msgq, client, &ev, 0) == -1) { DPRINTF(1, "vo: qdetached\n"); } return 0;}static int append_picture_q(int q_shmid, data_q_t **data_q){ data_q_t **data_q_p; for(data_q_p = data_q; *data_q_p != NULL; data_q_p =&(*data_q_p)->next); *data_q_p = malloc(sizeof(data_q_t)); if(attach_data_q(q_shmid, *data_q_p) == -1) { free(*data_q_p); *data_q_p = NULL; return -1; } return 0;}static int attach_picture_buffer(int q_shmid){ append_picture_q(q_shmid, &data_q_head); if(cur_data_q == NULL) { cur_data_q = data_q_head; } return 0;}static int detach_picture_q(int shmid){ //DNOTE("detach_picture_q q_shmid: %d\n", shmid); detach_data_q(shmid, &data_q_head); display_reset(); return 0;}static yuv_image_t *last_image_buf = NULL;static int redraw_needed = 0;void redraw_request(void){ redraw_needed = 1;}void redraw_done(void){ redraw_needed = 0;}static void redraw_screen(void){ if(flush_to_scrid != -1) { if(ctrl_time[video_scr_nr].scr_id < flush_to_scrid) { redraw_done(); return; } else { flush_to_scrid = -1; } } if(last_image_buf != NULL) { display(last_image_buf); } redraw_done();}void screenshot_request(ScreenshotMode_t mode){ screenshot_mode(mode); if(mode == 0) { if(last_image_buf) { if(last_image_buf->info->is_reference || (ctrl_data->speed < 1.0)) { redraw_request(); } } } else { redraw_request(); }}static int handle_events(MsgEventQ_t *q, MsgEvent_t *ev){ MsgEvent_t s_ev; switch(ev->type) { case MsgEventQNotify: if((cur_data_q->q_head != NULL) && (ev->notify.qid == cur_data_q->q_head->qid)) { ; } else { return 0; } break; case MsgEventQFlushData: DPRINTF(1, "vo: got flush\n"); flush_to_scrid = ev->flushdata.to_scrid; flush_subpicture(flush_to_scrid); break; case MsgEventQAttachQ: attach_picture_buffer(ev->attachq.q_shmid); break; case MsgEventQAppendQ: append_picture_q(ev->attachq.q_shmid, &data_q_head); break; case MsgEventQDetachQ: detach_picture_q(ev->detachq.q_shmid); s_ev.type = MsgEventQQDetached; s_ev.detachq.q_shmid = ev->detachq.q_shmid; if(MsgSendEvent(msgq, ev->detachq.client, &s_ev, 0) == -1) { DPRINTF(1, "vo: qdetached\n"); } wait_for_q_attach(); break; case MsgEventQCtrlData: attach_ctrl_shm(ev->ctrldata.shmid); break; case MsgEventQGntCapability: if((ev->gntcapability.capability & UI_DVD_GUI) == UI_DVD_GUI) gui_client = ev->gntcapability.capclient; else ERROR("capabilities not requested (%d)\n", ev->gntcapability.capability); break; case MsgEventQSetAspectModeSrc: aspect_mode = ev->setaspectmodesrc.mode_src; break; case MsgEventQSetSrcAspect: // hack, fix this to use an array of all aspectmodesrc's aspect_sender = ev->setsrcaspect.mode_src; aspect_new_frac_n = ev->setsrcaspect.aspect_frac_n; aspect_new_frac_d = ev->setsrcaspect.aspect_frac_d; break; case MsgEventQSetZoomMode: zoom_mode = ev->setzoommode.mode; redraw_request(); break; case MsgEventQReqInput: input_mask = ev->reqinput.mask; input_client = ev->reqinput.client; break; case MsgEventQSpeed: if(ctrl_time[prev_scr_nr].sync_master <= SYNC_VIDEO) { set_speed(&ctrl_time[prev_scr_nr].sync_point, ev->speed.speed); } break; case MsgEventQSaveScreenshot: if(ev->savescreenshot.formatstr[0]) { screenshot_set_formatstr(ev->savescreenshot.formatstr); } screenshot_request(ev->savescreenshot.mode); break; default: //DNOTE("unrecognized event type: %d\n", ev->type); return 0; break; } return 1;}void wait_until_handler(int sig) { end_of_wait = 1; return;}void alarm_handler(int sig) { end_of_wait = 1; if(last_image_buf) display_poll(last_image_buf);}static clocktime_t wait_until(clocktime_t *scr, sync_point_t *sp){ struct itimerval timer; clocktime_t time_left; clocktime_t real_time; struct sigaction act; struct sigaction oact; static clocktime_t last_ss_disable = {0,0}; timer.it_interval.tv_sec = 0; timer.it_interval.tv_usec = 0; while(1) { end_of_wait = 0; clocktime_get(&real_time); if(TIME_S(real_time) - TIME_S(last_ss_disable) > 50) { clocktime_t prof_time; if(talk_to_xscreensaver) { nudge_xscreensaver(); clocktime_get(&prof_time); timesub(&prof_time, &prof_time, &real_time); DNOTE("ss disable took %ld.%09ld s\n", TIME_S(prof_time), TIME_SS(prof_time)); } display_reset_screensaver(); TIME_S(last_ss_disable) = TIME_S(real_time); } calc_realtime_left_to_scrtime(&time_left, &real_time, scr, sp); /* fprintf(stderr, "rt: %ld.%09ld, scr: %ld.%09ld, left: %ld.%09ld\n", real_time.tv_sec, real_time.tv_nsec, scr->tv_sec, scr->tv_nsec, time_left.tv_sec, time_left.tv_nsec); */ if((TIME_S(time_left) > 0) || (TIME_SS(time_left) > (CT_FRACTION/10))) { // more then 100 ms left, lets wait and check x events every 100ms timer.it_value.tv_sec = 0; timer.it_value.tv_usec = 100000; act.sa_handler = alarm_handler; act.sa_flags = 0; sigaction(SIGALRM, &act, &oact); setitimer(ITIMER_REAL, &timer, NULL); } else if(TIME_SS(time_left) > min_time_left) { // less than 100ms but more than clktck/2 left, lets wait struct timespec sleeptime; #if 0 timer.it_value.tv_sec = 0; timer.it_value.tv_usec = TIME_SS(time_left)/(CT_FRACTION/1000000); act.sa_handler = wait_until_handler; act.sa_flags = 0; sigaction(SIGALRM, &act, &oact); setitimer(ITIMER_REAL, &timer, NULL);#endif sleeptime.tv_sec = TIME_S(time_left);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -