aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Input/in_dshow/dshowrender.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Plugins/Input/in_dshow/dshowrender.cpp')
-rw-r--r--Src/Plugins/Input/in_dshow/dshowrender.cpp583
1 files changed, 583 insertions, 0 deletions
diff --git a/Src/Plugins/Input/in_dshow/dshowrender.cpp b/Src/Plugins/Input/in_dshow/dshowrender.cpp
new file mode 100644
index 00000000..5a2b81dc
--- /dev/null
+++ b/Src/Plugins/Input/in_dshow/dshowrender.cpp
@@ -0,0 +1,583 @@
+//
+// This file contains code that supports an alternate method playing dshow data...
+// This will have dshow take control of rendering the data (audio or video).
+// Used for mms streams
+//
+#ifdef WINAMPX
+
+#include <windows.h>
+#include <math.h>
+
+#include "message.h"
+
+#include "../jnetlib/jnetlib.h"
+
+
+#include "in2.h"
+#include <AtlBase.h>
+#include <streams.h>
+#include <qedit.h>
+#include <qnetwork.h>
+#ifdef DEFGUID
+#include <initguid.h> // declares DEFINE_GUID to declare an EXTERN_C const.
+#endif
+
+#define IPC_GET_IVIDEOOUTPUT 500
+
+// Externs
+
+extern HRESULT AddToRot(IUnknown *pUnkGraph, DWORD *pdwRegister);
+extern void RemoveFromRot(DWORD pdwRegister);
+extern bool ReportMissingCodec(char *fn);
+
+extern In_Module mod; // the output module (filled in near the bottom of this file)
+
+extern char lastfn[MAX_PATH]; // currently playing file (used for getting info on the current file)
+extern char lastfn_status[256];
+extern int file_length; // file length, in bytes
+extern int decode_pos_ms; // current decoding position, in milliseconds.
+extern int paused; // are we paused?
+extern volatile int seek_needed; // if != -1, it is the point that the decode thread should seek to, in ms.
+extern int m_length;
+extern int paused; // are we paused?
+extern bool s_using_dsr;
+
+// Static Vars and Defines
+
+class IVideoOutput
+{
+public:
+ virtual ~IVideoOutput() { }
+ virtual int open(int w, int h, int vflip, double aspectratio, unsigned int fmt)=0;
+ virtual void setcallback(LRESULT (*msgcallback)(void *token, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam), void *token) { }
+ virtual void close()=0;
+ virtual void draw(void *frame)=0;
+ virtual void drawSubtitle(/*SubsItem **/ void *item) { }
+ virtual void showStatusMsg(const char *text) { }
+ virtual int get_latency() { return 0; }
+ virtual void notifyBufferState(int bufferstate) { } /* 0-255*/
+
+ virtual int extended(int param1, int param2, int param3) { return 0; } // Dispatchable, eat this!
+};
+static IVideoOutput * m_video_output;
+
+static CComPtr<IGraphBuilder> s_pGraphBuilder;
+static CComPtr<IMediaEventEx> s_pMediaEventEx;
+static CComPtr<IMediaControl> s_pMediaControl;
+static CComPtr<IVideoWindow> s_pVideoWindow;
+static CComPtr<IBasicAudio> s_pBasicAudio;
+static CComPtr<IBasicVideo> s_pBasicVideo;
+static CComPtr<IMediaSeeking> s_pMediaSeeking;
+
+static HWND s_dsr_notif_hwnd = NULL;
+static HWND s_hVideoWnd = NULL;
+static WNDPROC s_OriginalVideoWndProc = NULL;
+static RECT s_parentRect;
+static DWORD GraphEdit_dwRegister = 0;
+static int s_buffering = 0;
+static int s_bufferstat = 0;
+static BOOL s_bAudioOnly;
+static int s_setVolumeOnStart = -1;
+
+
+// Forward Declarations
+LRESULT CALLBACK dsr_TimerWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+LRESULT CALLBACK dsr_SubclassParentWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+void dsr_setvolume(int volume);
+void dsr_handleNotifyEvents();
+void dsr_stop();
+
+// Macros
+#define BeginEnumFilters(pFilterGraph, pEnumFilters, pBaseFilter) \
+{CComPtr<IEnumFilters> pEnumFilters; \
+ if(pFilterGraph && SUCCEEDED(pFilterGraph->EnumFilters(&pEnumFilters))) \
+{ \
+ for(CComPtr<IBaseFilter> pBaseFilter; S_OK == pEnumFilters->Next(1, &pBaseFilter, 0); pBaseFilter = NULL) \
+{ \
+
+#define EndEnumFilters }}}
+
+static void SendStatus(int status, int arg ) {
+ mod.fire_winampstatus( status, arg );
+}
+
+
+
+
+
+//------------------------------------------------------------------------------
+// Name: CheckVisibility()
+// Desc: Set global values for presence of video window.
+//------------------------------------------------------------------------------
+bool CheckVisibility(void) // returns false for failure
+{
+ HRESULT hr;
+ if ((!s_pVideoWindow) || (!s_pBasicVideo))
+ {
+ s_bAudioOnly = TRUE; // Audio-only files have no video interfaces.
+ return TRUE;
+ }
+ s_bAudioOnly = FALSE;
+
+ long lVisible;
+ hr = s_pVideoWindow->get_Visible(&lVisible); // If this is an audio-only clip, get_Visible() won't work.
+ if ((FAILED(hr)) && (hr == E_NOINTERFACE))
+ {
+ s_bAudioOnly = TRUE;
+ return TRUE;
+ }
+ return !FAILED(hr);
+}
+
+
+static RECT fitMediaToWindow(RECT rectWindow, int mediaWidth, int mediaHeight)
+{
+ RECT retval;
+
+ int windowWidth = rectWindow.right - rectWindow.left;
+ int windowHeight = rectWindow.bottom - rectWindow.top;
+
+ if (mediaHeight*windowWidth > mediaWidth*windowHeight)
+ {
+ // Gap is on left&right sides
+ int nOutWidth = windowHeight* mediaWidth / mediaHeight;
+ int nGap = (windowWidth - nOutWidth)/2;
+ retval.top = rectWindow.top;
+ retval.bottom = retval.top + windowHeight;
+ retval.left = rectWindow.left + nGap;
+ retval.right = retval.left + nOutWidth;
+ }
+ else
+ {
+ // Gap is on the top/bottom sides
+ int nOutHeight = windowWidth* mediaHeight / mediaWidth;
+ int nGap = (windowHeight - nOutHeight)/2;
+ retval.left = rectWindow.left;
+ retval.right = retval.left + windowWidth;
+ retval.top = rectWindow.top + nGap;
+ retval.bottom = retval.top + nOutHeight;
+ }
+
+ return retval;
+}
+
+
+
+
+void dsr_releaseObjects()
+{
+ if(s_dsr_notif_hwnd)
+ {
+ KillTimer(s_dsr_notif_hwnd,112);
+ DestroyWindow(s_dsr_notif_hwnd);
+ }
+ s_dsr_notif_hwnd=NULL;
+
+ if ((s_OriginalVideoWndProc) && (s_hVideoWnd))
+ {
+ SetWindowLong(s_hVideoWnd, GWL_WNDPROC, (LONG_PTR)s_OriginalVideoWndProc);
+ s_OriginalVideoWndProc = NULL;
+ s_hVideoWnd = NULL;
+ }
+
+ s_pMediaEventEx = NULL;
+ s_pGraphBuilder = NULL;
+ s_pMediaControl = NULL;
+ s_pVideoWindow = NULL;
+ s_pBasicAudio = NULL;
+ s_pBasicVideo = NULL;
+ s_pMediaSeeking = NULL;
+
+ s_using_dsr = false;
+}
+
+
+// Prefix used for functions here are dsd_ (
+
+int dsr_play(char *fn)
+{
+ HRESULT hr;
+
+ hr = s_pGraphBuilder.CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER);
+ if (FAILED(hr)) return 1;
+
+#ifdef _DEBUG
+ AddToRot(s_pGraphBuilder, &GraphEdit_dwRegister);
+#endif
+
+ s_pGraphBuilder->QueryInterface(IID_IMediaEvent, (void **)&s_pMediaEventEx);
+ if(!s_pMediaEventEx)
+ {
+ dsr_releaseObjects();
+ return 1;
+ }
+
+ m_video_output=(IVideoOutput *)SendMessage(mod.hMainWindow,WM_USER,0,IPC_GET_IVIDEOOUTPUT);
+ if(!m_video_output)
+ {
+ dsr_releaseObjects();
+ return -200; // Can't play file
+ }
+
+ // Create window that will receive filter notifications
+ static int classReg=0;
+ if(!classReg)
+ {
+ WNDCLASS wc={0,};
+ wc.style = CS_DBLCLKS;
+ wc.lpfnWndProc = dsr_TimerWndProc;
+ wc.hInstance = mod.hDllInstance;
+ wc.hIcon = NULL;
+ wc.hCursor = NULL;
+ wc.lpszClassName = "in_dshowClass2";
+ if (!RegisterClass(&wc))
+ {
+ dsr_releaseObjects();
+ return 1;
+ }
+ classReg=1;
+ }
+ s_dsr_notif_hwnd=CreateWindow("in_dshowClass2","dshow_notif2",NULL,0,0,1,1,NULL,NULL,mod.hDllInstance,NULL);
+ SetTimer(s_dsr_notif_hwnd,112,500,0);
+
+ // Build a normal graph (start rendering)
+ WCHAR f[4096];
+ MultiByteToWideChar(CP_ACP,0,fn,lstrlen(fn)+1,f,4096);
+ hr = s_pGraphBuilder->RenderFile(f,NULL);
+ if (FAILED(hr)) {
+ dsr_handleNotifyEvents();
+ dsr_releaseObjects();
+ if ((hr == CLASS_E_CLASSNOTAVAILABLE) || (hr == VFW_E_UNSUPPORTED_VIDEO) || (hr == VFW_E_NO_DECOMPRESSOR))
+ {
+ if (ReportMissingCodec(fn)) // returns true if we sent a message
+ return -500; // Unsupported format
+ return -200; // Can't play file
+ }
+ return 1;
+ }
+
+ // Check if it's a partial playing of the file (likely video codec missing)
+ if ((hr == VFW_S_PARTIAL_RENDER) || (hr == VFW_S_VIDEO_NOT_RENDERED))
+ {
+ if (!ReportMissingCodec(fn)) // Report the missing codec if we can determine it
+ mod.fire_winampstatus(WINAMPX_STATUS_MISSING_AVI_CODEC, 0); // If we can't report a null codec missing
+ }
+
+ if (FAILED(s_pGraphBuilder->QueryInterface(IID_IMediaControl, (void **)&s_pMediaControl)) ||
+ FAILED(s_pGraphBuilder->QueryInterface(IID_IMediaSeeking, (void **)&s_pMediaSeeking)) ||
+ FAILED(s_pMediaSeeking->SetTimeFormat(&TIME_FORMAT_MEDIA_TIME)))
+ {
+ dsr_releaseObjects();
+ return 1;
+ }
+
+
+ // Get length of file
+ int mylength = -1;
+ LONGLONG length;
+ if (SUCCEEDED(s_pMediaSeeking->GetDuration(&length)))
+ {
+ mylength=(int)(length/10000);
+ }
+ m_length = mylength;
+
+ // Query for video interfaces, which may not be relevant for audio files
+ s_pGraphBuilder->QueryInterface(IID_IVideoWindow, (void **)&s_pVideoWindow);
+ s_pGraphBuilder->QueryInterface(IID_IBasicVideo, (void **)&s_pBasicVideo);
+
+ // Query for audio interfaces, which may not be relevant for video-only files
+ s_pGraphBuilder->QueryInterface(IID_IBasicAudio, (void **)&s_pBasicAudio);
+ if (s_setVolumeOnStart != -1)
+ {
+ dsr_setvolume(s_setVolumeOnStart);
+ s_setVolumeOnStart = -1;
+ }
+
+ // Is this an audio-only file (no video component)?
+ CheckVisibility();
+ if (!s_bAudioOnly)
+ {
+ m_video_output->open(1, 1, 0, 1.0f, VIDEO_MAKETYPE('N','O','N','E')); // Dummy Size of 1x1
+ s_hVideoWnd = (HWND)m_video_output->extended(VIDUSER_GET_VIDEOHWND, 0, 0);
+ InvalidateRect(s_hVideoWnd, NULL, TRUE);
+
+ // Setup the video window
+ s_pVideoWindow->put_Owner((OAHWND) s_hVideoWnd);
+ s_pVideoWindow->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS);
+
+ RECT grc;
+ GetClientRect(s_hVideoWnd, &grc);
+ s_parentRect = grc;
+
+ if ((s_pBasicVideo) && (s_pVideoWindow))
+ {
+ long videoWidth, videoHeight;
+ if (SUCCEEDED(s_pBasicVideo->GetVideoSize(&videoWidth, &videoHeight)))
+ {
+ RECT r = fitMediaToWindow(grc, videoWidth, videoHeight);
+ s_pVideoWindow->SetWindowPosition(r.left, r.top, r.right-r.left, r.bottom-r.top);
+ }
+ }
+
+ // Intercept resize messages
+ s_OriginalVideoWndProc = (WNDPROC) SetWindowLong(s_hVideoWnd, GWL_WNDPROC, (LONG_PTR)dsr_SubclassParentWndProc);
+ }
+
+ // Run the graph to play the media file
+ hr = s_pMediaControl->Run();
+ if (FAILED(hr))
+ {
+ dsr_stop();
+ return 1;
+ }
+
+ return 0;
+}
+
+
+// stop playing.
+void dsr_stop()
+{
+ if (s_pMediaControl)
+ s_pMediaControl->Stop();
+ if (s_pVideoWindow)
+ {
+ s_pVideoWindow->put_Visible(OAFALSE);
+ s_pVideoWindow->put_Owner(NULL);
+ }
+
+ if (m_video_output)
+ m_video_output->close();
+ mod.outMod->Close();
+ mod.SAVSADeInit();
+ dsr_releaseObjects();
+ m_length=-1;
+ lastfn[0]=0;
+}
+
+
+
+// Standard Pause Routines
+void dsr_pause() {
+ paused=1;
+ if (s_pMediaControl)
+ {
+ s_pMediaControl->Pause();
+ }
+}
+void dsr_unpause() {
+ paused=0;
+ if (s_pMediaControl)
+ {
+ s_pMediaControl->Run();
+ }
+}
+
+
+
+int dsr_getoutputtime() {
+ if (s_bufferstat)
+ return s_bufferstat;
+
+ if (s_pMediaSeeking)
+ {
+ LONGLONG pos;
+ if (SUCCEEDED(s_pMediaSeeking->SetTimeFormat(&TIME_FORMAT_MEDIA_TIME)) &&
+ SUCCEEDED(s_pMediaSeeking->GetCurrentPosition(&pos)))
+ {
+ return (int)(pos/10000);
+ }
+ }
+ return 0;
+}
+
+void dsr_setoutputtime(int time_in_ms) {
+ if(s_pMediaSeeking) {
+ DWORD dwCaps = AM_SEEKING_CanSeekAbsolute;
+ if (s_pMediaSeeking->CheckCapabilities(&dwCaps) == S_OK)
+ {
+ int oldpause=paused;
+ if(oldpause) dsr_unpause();
+ LONGLONG l=((LONGLONG)time_in_ms)*10000;
+ s_pMediaSeeking->SetTimeFormat(&TIME_FORMAT_MEDIA_TIME);
+ s_pMediaSeeking->SetPositions(&l,AM_SEEKING_AbsolutePositioning,NULL,AM_SEEKING_NoPositioning);
+ mod.outMod->Flush(time_in_ms);
+ if(oldpause) dsr_pause();
+ }
+ }
+}
+
+void dsr_setvolume(int volume)
+{
+ if (s_pBasicAudio)
+ {
+ volume = min(volume, 255);
+ volume = max(volume, 0);
+ s_pBasicAudio->put_Volume(log(volume+1)/log(256)*10000-10000); // Map (0,255) to (-10000,0) log scale (as ActiveX is in DB's)
+ }
+ else
+ {
+ s_setVolumeOnStart = volume;
+ }
+}
+
+
+
+LRESULT CALLBACK dsr_TimerWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ if(uMsg==WM_TIMER && wParam==112 && s_dsr_notif_hwnd)
+ {
+ dsr_handleNotifyEvents();
+ if(s_buffering)
+ {
+ BeginEnumFilters(s_pGraphBuilder, pEF, pBF)
+ {
+ if(CComQIPtr<IAMNetworkStatus, &IID_IAMNetworkStatus> pAMNS = pBF)
+ {
+ long BufferingProgress = 0;
+ if(SUCCEEDED(pAMNS->get_BufferingProgress(&BufferingProgress)) && BufferingProgress > 0)
+ {
+ wsprintf(lastfn_status,"Buffering (%ld%%)",BufferingProgress);
+ if(m_video_output) m_video_output->extended(VIDUSER_SET_INFOSTRING,(int)&lastfn_status,0);
+
+ int bpos=BufferingProgress;
+ int csa = mod.SAGetMode();
+ char tempdata[75*2]={0,};
+ int x;
+ if (csa&1)
+ {
+ for (x = 0; x < bpos*75/100; x ++)
+ {
+ tempdata[x]=x*16/75;
+ }
+ }
+ if (csa&2)
+ {
+ int offs=(csa&1) ? 75 : 0;
+ x=0;
+ while (x < bpos*75/100)
+ {
+ tempdata[offs + x++]=-6+x*14/75;
+ }
+ while (x < 75)
+ {
+ tempdata[offs + x++]=0;
+ }
+ }
+ if (csa==4)
+ {
+ tempdata[0]=tempdata[1]=(bpos*127/100);
+ }
+ if (csa) mod.SAAdd(tempdata,++s_bufferstat,(csa==3)?0x80000003:csa);
+
+ PostMessage(mod.hMainWindow,WM_USER,0,243);
+ SendStatus(WINAMPX_STATUS_PREBUFFERING_PCT, 255*BufferingProgress/100);
+
+ break;
+ }
+ }
+ }
+ EndEnumFilters
+ }
+ }
+
+ return (DefWindowProc(hwnd, uMsg, wParam, lParam));
+}
+
+
+void dsr_handleNotifyEvents()
+{
+// char s[256];
+ for (;;) {
+ long evCode, param1, param2;
+ HRESULT h = s_pMediaEventEx->GetEvent(&evCode, &param1, &param2, 0);
+ if (FAILED(h)) break;
+ switch(evCode) {
+ case EC_BUFFERING_DATA:
+ {
+// sprintf(s, "Handling Event: EC_BUFFERING_DATA: %d\n", param1);
+// OutputDebugString(s);
+ s_buffering=param1;
+ if(!s_buffering)
+ {
+ lastfn_status[0]=0;
+ s_bufferstat=0;
+ PostMessage(mod.hMainWindow,WM_USER,0,243);
+
+ break;
+ }
+ }
+ break;
+
+ case EC_COMPLETE:
+ {
+// OutputDebugString("Handling Event: EC_COMPLETE\n");
+ PostMessage(mod.hMainWindow,WM_WA_MPEG_EOF,0,0);
+ }
+
+ default:
+ {
+// sprintf(s, "Handling Event: 0x%x : param1=0x%x; param2 = 0x%x\n", evCode, param1, param2);
+// OutputDebugString(s);
+ }
+ }
+
+ s_pMediaEventEx->FreeEventParams(evCode, param1, param2);
+ }
+}
+
+
+// Subclass of the Window containing the code. (Used to check if the parent got resized)
+LRESULT CALLBACK dsr_SubclassParentWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ LRESULT retval = CallWindowProc(s_OriginalVideoWndProc, hwnd, uMsg, wParam, lParam);
+
+ if ((uMsg == WM_SIZE) && (s_hVideoWnd))
+ {
+ RECT grc;
+ GetClientRect(s_hVideoWnd, &grc);
+
+ if (!EqualRect(&grc, &s_parentRect))
+ {
+ s_parentRect = grc;
+
+ if ((s_pBasicVideo) && (s_pVideoWindow))
+ {
+ long videoWidth, videoHeight;
+ if (SUCCEEDED(s_pBasicVideo->GetVideoSize(&videoWidth, &videoHeight)))
+ {
+ RECT r = fitMediaToWindow(grc, videoWidth, videoHeight);
+ s_pVideoWindow->SetWindowPosition(r.left, r.top, r.right-r.left, r.bottom-r.top);
+ }
+ }
+ }
+ }
+
+ if ((uMsg == WM_PARENTNOTIFY) && (LOWORD(wParam)==WM_RBUTTONDOWN))
+ {
+ SendStatus(WINAMPX_STATUS_VIDEO_RIGHT_CLICK, lParam);
+ }
+
+ if ((uMsg == WM_PARENTNOTIFY) && (LOWORD(wParam)==WM_LBUTTONDOWN))
+ {
+ static DWORD dwLastTime = 0;
+ int dwTimeNow = GetTickCount();
+ if (((dwTimeNow - dwLastTime) < GetDoubleClickTime()) && (dwLastTime != 0))
+ {
+ PostMessage(hwnd, WM_USER_DSR_LBUTTONDBLCLK, 0, 0); // Notify the video window that we double clicked
+ dwLastTime = 0;
+ }
+ else
+ dwLastTime = dwTimeNow;
+ }
+
+ if ((uMsg == WM_USER_DSR_FULLSCREEN) && (s_pVideoWindow))
+ {
+ s_pVideoWindow->HideCursor(wParam ? OATRUE : OAFALSE);
+ }
+
+ return retval;
+}
+
+#endif // WINAMPX