#include "Main.h" #include "VideoLayer.h" #include #include #include #include "util.h" #include "resource.h" #include #include "config.h" #define VIDEO_ACCEPTABLE_DROP (config_video_drop_threshold*10000) VideoLayer::VideoLayer(IWMReader *_reader) : reader(_reader), videoOutputNum(-1), reader2(0), offset(0), nextRest(0), converter(NULL), videoOpened(false), video_output_opened(false), killSwitch(0), aspect(0), earlyDelivery(0), fourcc(0), drmProtected(false), videoStream(0), flip(false), videoWidth(0), videoHeight(0) { reader->AddRef(); if (FAILED(reader->QueryInterface(&reader2))) reader2 = 0; if (FAILED(reader->QueryInterface(&header))) header = 0; killSwitch = CreateEvent(NULL, TRUE, FALSE, NULL); } VideoLayer::~VideoLayer() { videoThread.Kill(); if (reader2) reader2->Release(); if (header) header->Release(); reader->Release(); CloseHandle(killSwitch); } bool AcceptableFormat(GUID &subtype) { if (subtype == WMMEDIASUBTYPE_YV12 || subtype == WMMEDIASUBTYPE_YUY2 || subtype == WMMEDIASUBTYPE_UYVY //|| subtype == WMMEDIASUBTYPE_YVYU || subtype == WMMEDIASUBTYPE_RGB24 || subtype == WMMEDIASUBTYPE_RGB32 || subtype == WMMEDIASUBTYPE_I420 || subtype == WMMEDIASUBTYPE_IYUV || subtype == WMMEDIASUBTYPE_RGB1 || subtype == WMMEDIASUBTYPE_RGB4 || subtype == WMMEDIASUBTYPE_RGB8 || subtype == WMMEDIASUBTYPE_RGB565 || subtype == WMMEDIASUBTYPE_RGB555 ) return true; else return false; } bool VideoLayer::AttemptOpenVideo(VideoOutputStream *attempt) { videoWidth = attempt->DestinationWidth(); videoHeight = attempt->DestinationHeight(); flip = attempt->Flipped(); fourcc = attempt->FourCC(); if (!fourcc) return false; aspect = 1.0; return true; } bool VideoLayer::OpenVideo() { videoOutputNum = -1; DWORD numOutputs, numFormats; IWMOutputMediaProps *formatProperties; VideoOutputStream *stream; GUID mediaType; reader->GetOutputCount(&numOutputs); for (DWORD output = 0;output < numOutputs;output++) { // test the default format first, and if that fails, iterate through the rest const int defaultFormat = -1; HRESULT hr; if (FAILED(hr = reader->GetOutputFormatCount(output, &numFormats))) continue; for (int format = 0/*defaultFormat*/;format != numFormats;format++) { if (format == defaultFormat) reader->GetOutputProps(output, &formatProperties); else reader->GetOutputFormat(output, format, &formatProperties); formatProperties->GetType(&mediaType); if (mediaType == WMMEDIATYPE_Video) { stream = new VideoOutputStream(formatProperties); if (stream->IsVideo() // if it's video && AcceptableFormat(stream->GetSubType()) // and a video format we like && AttemptOpenVideo(stream)) // and winamp was able to open it { videoOpened = true; int fourcc = stream->FourCC(); if (fourcc == '8BGR') { RGBQUAD *palette = stream->CreatePalette(); winamp.SetVideoPalette(palette); // TODO: don't leak the palette } char *cc = (char *) & fourcc; char status[512] = {0}; StringCchPrintfA(status, 512, WASABI_API_LNGSTRING(IDS_WINDOWS_MEDIA_XXX), stream->DestinationWidth(), stream->DestinationHeight(), cc[0], cc[1], cc[2], cc[3]); winamp.SetVideoStatusText(status); converter = MakeConverter(stream); videoOutputNum = output; videoStream = stream; reader->SetOutputProps(output, formatProperties); formatProperties->Release(); return true; } delete stream; stream = 0; } formatProperties->Release(); } } return false; } void VideoLayer::SampleReceived(QWORD &timeStamp, QWORD &duration, unsigned long &outputNum, unsigned long &flags, INSSBuffer *&sample) { if (outputNum == videoOutputNum) { if (WaitForSingleObject(killSwitch, 0) == WAIT_OBJECT_0) return ; INSSBuffer3 *buff3; if (SUCCEEDED(sample->QueryInterface(&buff3))) { short aspectHex = 0; DWORD size = 2; buff3->GetProperty(WM_SampleExtensionGUID_PixelAspectRatio, &aspectHex, &size); if (aspectHex) { double newAspect = (double)((aspectHex & 0xFF00) >> 8) / (double)(aspectHex & 0xFF) ; if (newAspect != aspect) { aspect = newAspect; videoThread.OpenVideo(drmProtected, videoWidth, videoHeight, flip, aspect, fourcc); video_output_opened=true; } } buff3->Release(); } if (!video_output_opened) { videoThread.OpenVideo(drmProtected, videoWidth, videoHeight, flip, aspect, fourcc); video_output_opened=true; } __int64 timeDiff; First().TimeToSync(timeStamp, timeDiff); if (timeDiff < -VIDEO_ACCEPTABLE_DROP) // late { timeDiff = -timeDiff; if (config_video_catchup) First().VideoCatchup(timeDiff); if (config_video_framedropoffset) this->VideoFrameDrop((DWORD)(timeDiff / 10000)); if (config_video_notifylate) reader2->NotifyLateDelivery(timeDiff); // drop the frame } else // early { while (!videoThread.AddBuffer(sample, timeStamp, flags, drmProtected)) { if (WaitForSingleObject(killSwitch, VIDEO_ACCEPTABLE_JITTER_MS) == WAIT_OBJECT_0) return ; } } } else WMHandler::SampleReceived(timeStamp, duration, outputNum, flags, sample); } void VideoLayer::Opened() { WORD stream = 0; WMT_ATTR_DATATYPE type = WMT_TYPE_BOOL; BOOL value; WORD valueLen = sizeof(value); header->GetAttributeByName(&stream, g_wszWMProtected, &type, (BYTE *)&value, &valueLen); drmProtected = !!value; ResetEvent(killSwitch); if (OpenVideo()) { ResetEvent(killSwitch); HRESULT hr; BOOL dedicatedThread = config_video_dedicated_thread ? TRUE : FALSE; hr = reader2->SetOutputSetting(videoOutputNum, g_wszDedicatedDeliveryThread, WMT_TYPE_BOOL, (BYTE *) & dedicatedThread, sizeof(dedicatedThread)); assert(hr == S_OK); earlyDelivery = config_video_early ? config_video_early_pad : 0; hr = reader2->SetOutputSetting(videoOutputNum, g_wszEarlyDataDelivery, WMT_TYPE_DWORD, (BYTE *) & earlyDelivery , sizeof(earlyDelivery)); assert(hr == S_OK); BOOL outOfOrder = config_video_outoforder ? TRUE : FALSE; hr = reader2->SetOutputSetting(videoOutputNum, g_wszDeliverOnReceive, WMT_TYPE_BOOL, (BYTE *) & outOfOrder, sizeof(outOfOrder)); assert(hr == S_OK); BOOL justInTime = config_lowmemory ? TRUE : FALSE; hr = reader2->SetOutputSetting(videoOutputNum, g_wszJustInTimeDecode, WMT_TYPE_BOOL, (BYTE *) & justInTime, sizeof(justInTime)); assert(hr == S_OK); } else { videoOpened = false; } WMHandler::Opened(); } void VideoLayer::VideoFrameDrop(DWORD lateness) { //earlyDelivery+=lateness; lateness += earlyDelivery; HRESULT hr = reader2->SetOutputSetting(videoOutputNum, g_wszEarlyDataDelivery, WMT_TYPE_DWORD, (BYTE *) & lateness, sizeof(lateness)); assert(hr == S_OK); } void VideoLayer::Closed() { if (video_output_opened) { videoThread.CloseVideo(drmProtected); video_output_opened = false; } videoOpened = false; delete videoStream; videoStream=0; WMHandler::Closed(); } bool VideoLayer::IsOpen() { return videoOpened; } void VideoLayer::HasVideo(bool &video) { video=videoOpened; } void VideoLayer::Kill() { SetEvent(killSwitch); if (videoOpened) videoThread.SignalStop();//SignalStop(); WMHandler::Kill(); if (videoOpened) videoThread.WaitForStop(); } void VideoLayer::Started() { ResetEvent(killSwitch); if (videoOpened) videoThread.Start(converter, &First()); WMHandler::Started(); } void VideoLayer::Stopped() { if (videoOpened) videoThread.Stop(); WMHandler::Stopped(); }