📄 videohandler.cc
字号:
//// Copyright (c) 2004 by Istv醤 V醨adi//// This file is part of dxr3Player, a DVD player written specifically // for the DXR3 (aka Hollywood+) decoder card.// 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 "VideoHandler.h"#include "SPUHandler.h"#include "output/SystemTimer.h"#include "dvd/packet/PacketQueue.h"#include "dvd/packet/TimedDataBlockPacket.h"#include "dvd/packet/DataBlockTypes.h"#include "input/SDLInputHandler.h"#include "util/Reference.h"#include "util/Util.h"#include "util/Config.h"#include <X11/Xlib.h>#include <cstring>//------------------------------------------------------------------------------using output::sdl::VideoHandler;using output::SystemTimer;using dvd::packet::PacketQueue;using dvd::packet::TimedDataBlockPacket;using dvd::packet::DataBlockTypes;using input::SDLInputHandler;using sched::Scheduler;//------------------------------------------------------------------------------VideoHandler::VideoHandler(PacketQueue& inputQueue, SystemTimer& timer, SPUHandler& spuHandler) : Schedulable("output::sdl::VideoHandler"), inputQueue(inputQueue), timer(timer), spuHandler(spuHandler), mpeg2dec(0), mpeg2info(0), basePTS(0), nextIFramePTS(INVALID_PTS), numFields(0), playIntra(false), nextOnlyIFrame(true), screen(0), overlay(0), isFullScreen(false), brightness(Config::get().defaultBrightness*2), currentBuffer(0){ const Config& config = Config::get(); windowWidth = config.initialWindowWidth; windowHeight = config.initialWindowHeight; Display* display = XOpenDisplay(0); if (display!=0) { displayWidth = DisplayWidth(display, DefaultScreen(display)); displayHeight = DisplayHeight(display, DefaultScreen(display)); XCloseDisplay(display); } else { displayWidth = 800; displayHeight = 600; } Log::debug("output::sdl::VideoHandler: displayWidth=%u, displayHeight=%u\n", displayWidth, displayHeight); for(size_t i = 0; i<3; ++i) { for(size_t j = 0; j<3; ++j) { frameBuffers[i][j] = 0; } } setupGamma();}//------------------------------------------------------------------------------void VideoHandler::run(){ mpeg2dec = mpeg2_init(); mpeg2info = mpeg2_info(mpeg2dec); parseMPEG(); SDL_Init(SDL_INIT_VIDEO|SDL_INIT_NOPARACHUTE); createScreen(); while(!shouldQuit()) { clearInterrupt(); SDL_Event event; while(SDL_PollEvent(&event)) { if (event.type==SDL_KEYDOWN) { SDLInputHandler::get().processEvent(event); } else if (event.type==SDL_VIDEORESIZE) { const Config& config = Config::get(); windowWidth = event.resize.w; windowHeight = event.resize.w * config.initialWindowHeight / config.initialWindowWidth; createScreen(); } } Reference<TimedDataBlockPacket> packet = TimedDataBlockPacket::convert(inputQueue.get(Util::currentTimeMillis()+100)); if (!packet.isValid()) { if (!isInterrupted()) { redrawCurrentFrame(); } continue; } assert(packet->getType()==DataBlockTypes::video); pts_t packetPTS = packet->getPTS(); if (packetPTS!=INVALID_PTS) { nextIFramePTS = packetPTS; } uint8_t* data = const_cast<uint8_t*>(packet->getData()); mpeg2_buffer(mpeg2dec, data, data + packet->getLength()); parseMPEG(); } destroyScreen(); SDL_Quit(); mpeg2_close(mpeg2dec);}//------------------------------------------------------------------------------void VideoHandler::printStatus() const{ Log::debug(" brightness: %u\n", brightness/2);}//------------------------------------------------------------------------------void VideoHandler::reset(){ flush(); nextIFramePTS = INVALID_PTS; nextOnlyIFrame = true; mpeg2_close(mpeg2dec); mpeg2dec = mpeg2_init(); mpeg2info = mpeg2_info(mpeg2dec); parseMPEG(); interrupt();}//------------------------------------------------------------------------------void VideoHandler::flush(){ basePTS = 0; numFields = 0;}//------------------------------------------------------------------------------void VideoHandler::setAttributes(const VideoAttributes& attributes){ videoAttributes = attributes; spuHandler.setWideScreen(videoAttributes.aspectRatio == AR_16_9);} //------------------------------------------------------------------------------void VideoHandler::toggleFullScreen(){ isFullScreen = !isFullScreen; createScreen();}//------------------------------------------------------------------------------unsigned VideoHandler::increaseBrightness(){ if (brightness<200) { brightness += 10; } setupGamma(); return brightness/2;}//------------------------------------------------------------------------------unsigned VideoHandler::decreaseBrightness(){ if (brightness>0) { brightness -= 10; } setupGamma(); return brightness/2;}//------------------------------------------------------------------------------unsigned VideoHandler::increaseContrast(){ return 50;}//------------------------------------------------------------------------------unsigned VideoHandler::decreaseContrast(){ return 50;}//------------------------------------------------------------------------------unsigned VideoHandler::increaseSaturation(){ return 50;}//------------------------------------------------------------------------------unsigned VideoHandler::decreaseSaturation(){ return 50;}//------------------------------------------------------------------------------void VideoHandler::createScreen(){ bool restoreOverlay = overlay!=0; size_t overlayWidth = 0; size_t overlayHeight = 0; if (restoreOverlay) { overlayWidth = overlay->w; overlayHeight = overlay->h; SDL_FreeYUVOverlay(overlay); } size_t screenWidth = isFullScreen ? displayWidth : windowWidth; size_t screenHeight = isFullScreen ? (screenWidth * 3 / 4) : windowHeight; int flags = SDL_HWSURFACE|SDL_DOUBLEBUF; if (isFullScreen) flags |= SDL_FULLSCREEN; else flags |= SDL_RESIZABLE; screen = SDL_SetVideoMode(screenWidth, screenHeight, 0, flags); SDL_ShowCursor(isFullScreen ? SDL_DISABLE : SDL_ENABLE); if (restoreOverlay) { overlay = SDL_CreateYUVOverlay(overlayWidth, overlayHeight, SDL_YV12_OVERLAY, screen); }}//------------------------------------------------------------------------------void VideoHandler::setupOverlay(size_t width, size_t height){ bool isWideScreen = videoAttributes.aspectRatio==AR_16_9; size_t overlayWidth = width; size_t overlayHeight = isWideScreen ? width : height; pictureHeight = height; if (overlay==0 || static_cast<size_t>(overlay->w)!=overlayWidth || static_cast<size_t>(overlay->h)!=overlayHeight) { if (overlay!=0) { SDL_FreeYUVOverlay(overlay); } overlay = SDL_CreateYUVOverlay(overlayWidth, overlayHeight, SDL_YV12_OVERLAY, screen); SDL_LockYUVOverlay(overlay); size_t numPixels = overlayWidth * overlayHeight; memset(overlay->pixels[0], 16, numPixels); memset(overlay->pixels[1], 128, numPixels/4); memset(overlay->pixels[2], 128, numPixels/4); SDL_UnlockYUVOverlay(overlay); SDL_DisplayYUVOverlay(overlay, &screen->clip_rect); }}//------------------------------------------------------------------------------void VideoHandler::destroyScreen(){ if (overlay!=0) { SDL_FreeYUVOverlay(overlay); overlay = 0; }}//------------------------------------------------------------------------------void VideoHandler::parseMPEG(){ int state = mpeg2_parse(mpeg2dec); while(state!=STATE_BUFFER && !isInterrupted()) { const mpeg2_sequence_t* sequence = mpeg2info->sequence; switch(state) { case STATE_SEQUENCE: for(size_t i = 0; i<3; ++i) { free(frameBuffers[i][0]); frameBuffers[i][0] = (uint8_t*)malloc(sequence->width * sequence->height); free(frameBuffers[i][1]); frameBuffers[i][1] = (uint8_t*)malloc(sequence->chroma_width * sequence->chroma_height); free(frameBuffers[i][2]); frameBuffers[i][2] = (uint8_t*)malloc(sequence->chroma_width * sequence->chroma_height); mpeg2_set_buf(mpeg2dec, frameBuffers[i], 0); } currentBuffer = 0; break; case STATE_SLICE: case STATE_END: case STATE_INVALID_END: if (mpeg2info->display_fbuf) { unsigned pictureFlags = mpeg2info->display_picture->flags; bool isIFrame = (pictureFlags&PIC_MASK_CODING_TYPE)==PIC_FLAG_CODING_TYPE_I; if ((playIntra || nextOnlyIFrame) && !isIFrame) { break; } nextOnlyIFrame = false; currentBuffer = mpeg2info->display_fbuf->buf; setupOverlay(sequence->width, sequence->height); drawCurrentFrame(); size_t frameLength = (videoAttributes.standard==PAL) ? 3600 : 3003; pts_t framePTS = basePTS + numFields * frameLength / 2; if (isIFrame && nextIFramePTS!=INVALID_PTS) { pts_t picturePTS = nextIFramePTS + timer.getPTSCompensation(); if (framePTS!=picturePTS) { Log::debug("output::sdl::VideoHandler::parseMPEG: framePTS=%llu, picturePTS=%llu, basePTS=%llu, numFields=%u\n", framePTS, picturePTS, basePTS, numFields); framePTS = picturePTS; basePTS = picturePTS; numFields = 0; } nextIFramePTS = INVALID_PTS; } pts_t currentSCR = timer.getCurrentSCR(); if (framePTS<currentSCR) { pts_t tolerance = playIntra ? Util::ptsFrequency : (2*frameLength); if ( (framePTS+tolerance) < currentSCR ) { ptsdiff_t diff = currentSCR - framePTS; timer.advancePTSCompensation(diff); basePTS += diff; Log::debug("output::sdl::VideoHandler::parseMPEG: advanced PTS compensation by: %lld\n", diff); } } else { timer.sleepInterruptible(framePTS); if (isInterrupted()) return; } if (framePTS>=currentSCR || playIntra) { SDL_DisplayYUVOverlay(overlay, &screen->clip_rect); } else { Log::debug("output::sdl::VideoHandler::parseMPEG: skipped the display of a frame\n"); } numFields += mpeg2info->display_picture->nb_fields; } break; } Scheduler::yield("output::sdl::VideoHandler::parseMPEG"); if (!isInterrupted()) { state = mpeg2_parse(mpeg2dec); } }}//------------------------------------------------------------------------------void VideoHandler::drawCurrentFrame(){ assert(overlay!=0); assert(currentBuffer!=0); SDL_LockYUVOverlay(overlay); size_t numPixels = overlay->w * pictureHeight; size_t overlaySize = overlay->h * overlay->w; size_t stripSize = (overlaySize - numPixels) / 2; size_t bottomStripBegin = (overlaySize + numPixels) / 2; memset(overlay->pixels[0], 16, stripSize); memset(overlay->pixels[0]+bottomStripBegin, 16, stripSize); if (brightness==100) { memcpy(overlay->pixels[0] + stripSize, currentBuffer[0], numPixels); } else { uint8_t* dest = overlay->pixels[0] + stripSize; uint8_t* src = currentBuffer[0]; for(size_t i = 0; i<numPixels; ++i, dest++, src++) { *dest = gamma_y[*src]; } } numPixels/=4; stripSize/=4; bottomStripBegin/=4; memset(overlay->pixels[1], 128, stripSize); memset(overlay->pixels[1] + bottomStripBegin, 128, stripSize); memcpy(overlay->pixels[1] + stripSize, currentBuffer[2], numPixels); memset(overlay->pixels[2], 128, stripSize); memset(overlay->pixels[2] + bottomStripBegin, 128, stripSize); memcpy(overlay->pixels[2] + stripSize, currentBuffer[1], numPixels); spuHandler.embedInto(overlay); SDL_UnlockYUVOverlay(overlay);}//------------------------------------------------------------------------------void VideoHandler::redrawCurrentFrame(){ if (overlay!=0 && currentBuffer!=0 && !playIntra) { drawCurrentFrame(); SDL_DisplayYUVOverlay(overlay, &screen->clip_rect); }}//------------------------------------------------------------------------------void VideoHandler::setupGamma(){ if (brightness==100) return; for(size_t i = 0; i<256; ++i) { unsigned x = i * brightness / 100; if (x>255) x = 255; gamma_y[i] = x; }}//------------------------------------------------------------------------------//------------------------------------------------------------------------------
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -