/*
* $Id$
*
* (C) 2003-2005 Gabest
*
* This file is part of asf2mkv.
*
* Asf2mkv 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 3 of the License, or
* (at your option) any later version.
*
* Asf2mkv 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, see .
*
*/
#include "stdafx.h"
#include "asf2mkv.h"
#include "asf2mkvDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
#include
#include "./asf2mkvdlg.h"
// {6B6D0800-9ADA-11d0-A520-00A0D10129C0}
DEFINE_GUID(CLSID_NetShowSource,
0x6b6d0800, 0x9ada, 0x11d0, 0xa5, 0x20, 0x0, 0xa0, 0xd1, 0x1, 0x29, 0xc0);
// CAboutDlg dialog used for App About
class CAboutDlg : public CDialog
{
public:
CAboutDlg();
// Dialog Data
enum { IDD = IDD_ABOUTBOX };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
// Implementation
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
END_MESSAGE_MAP()
// CUrlDropTarget
BEGIN_MESSAGE_MAP(CUrlDropTarget, COleDropTarget)
END_MESSAGE_MAP()
DROPEFFECT CUrlDropTarget::OnDragEnter(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point)
{
return DROPEFFECT_NONE;
}
DROPEFFECT CUrlDropTarget::OnDragOver(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point)
{
UINT CF_URL = RegisterClipboardFormat(_T("UniformResourceLocator"));
return pDataObject->IsDataAvailable(CF_URL) ? DROPEFFECT_COPY : DROPEFFECT_NONE;
}
BOOL CUrlDropTarget::OnDrop(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point)
{
UINT CF_URL = RegisterClipboardFormat(_T("UniformResourceLocator"));
BOOL bResult = FALSE;
if(pDataObject->IsDataAvailable(CF_URL))
{
FORMATETC fmt = {CF_URL, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
if(HGLOBAL hGlobal = pDataObject->GetGlobalData(CF_URL, &fmt))
{
LPCSTR pText = (LPCSTR)GlobalLock(hGlobal);
if(AfxIsValidString(pText))
{
AfxGetMainWnd()->SendMessage(WM_OPENURL, 0, (LPARAM)pText);
GlobalUnlock(hGlobal);
bResult = TRUE;
}
}
}
return bResult;
}
DROPEFFECT CUrlDropTarget::OnDropEx(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropDefault, DROPEFFECT dropList, CPoint point)
{
return (DROPEFFECT)-1;
}
void CUrlDropTarget::OnDragLeave(CWnd* pWnd)
{
}
DROPEFFECT CUrlDropTarget::OnDragScroll(CWnd* pWnd, DWORD dwKeyState, CPoint point)
{
return DROPEFFECT_NONE;
}
// Casf2mkvDlg dialog
#define WM_GRAPHNOTIFY (WM_APP+1)
Casf2mkvDlg::Casf2mkvDlg(CWnd* pParent /*=NULL*/)
: CResizableDialog(Casf2mkvDlg::IDD, pParent)
, m_fRecording(false)
, m_mru(0, _T("MRU"), _T("file%d"), 20, 10)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void Casf2mkvDlg::DoDataExchange(CDataExchange* pDX)
{
__super::DoDataExchange(pDX);
DDX_Control(pDX, IDC_COMBO1, m_combo);
DDX_Control(pDX, IDC_STATIC1, m_video);
}
void Casf2mkvDlg::SetupCombo()
{
m_combo.ResetContent();
for(int i = 0; i < m_mru.GetSize(); i++)
if(!m_mru[i].IsEmpty())
m_combo.AddString(m_mru[i]);
}
void Casf2mkvDlg::SetVideoRect()
{
if(pVW)
{
CRect r;
m_video.GetWindowRect(r);
r -= r.TopLeft();
pVW->SetWindowPosition(r.left, r.top, r.Width(), r.Height());
}
}
BEGIN_MESSAGE_MAP(Casf2mkvDlg, CResizableDialog)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_MESSAGE(WM_GRAPHNOTIFY, OnGraphNotify)
ON_BN_CLICKED(IDC_BUTTON1, OnRecord)
ON_UPDATE_COMMAND_UI(IDC_BUTTON1, OnUpdateRecord)
ON_UPDATE_COMMAND_UI(IDC_COMBO1, OnUpdateFileName)
ON_UPDATE_COMMAND_UI(IDC_BUTTON2, OnUpdateSettings)
ON_UPDATE_COMMAND_UI(IDC_CHECK2, OnUpdateSettings)
ON_WM_SIZE()
ON_WM_TIMER()
ON_BN_CLICKED(IDC_BUTTON2, OnBnClickedButton2)
ON_MESSAGE(WM_APP, OnUrlOpen)
END_MESSAGE_MAP()
// Casf2mkvDlg message handlers
BOOL Casf2mkvDlg::OnInitDialog()
{
__super::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
AddAnchor(IDC_STATIC1, TOP_LEFT, BOTTOM_RIGHT);
AddAnchor(IDC_COMBO1, BOTTOM_LEFT, BOTTOM_RIGHT);
AddAnchor(IDC_BUTTON1, BOTTOM_RIGHT);
AddAnchor(IDC_BUTTON2, BOTTOM_RIGHT);
m_mru.ReadList();
SetupCombo();
m_video.ModifyStyle(0, WS_CLIPCHILDREN);
SetWindowText(ResStr(IDS_TITLE));
m_urlDropTarget.Register(this);
return TRUE; // return TRUE unless you set the focus to a control
}
BOOL Casf2mkvDlg::DestroyWindow()
{
m_urlDropTarget.Revoke();
return __super::DestroyWindow();
}
void Casf2mkvDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
__super::OnSysCommand(nID, lParam);
}
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void Casf2mkvDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, reinterpret_cast(dc.GetSafeHdc()), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
__super::OnPaint();
}
}
// The system calls this function to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR Casf2mkvDlg::OnQueryDragIcon()
{
return static_cast(m_hIcon);
}
LRESULT Casf2mkvDlg::OnGraphNotify(WPARAM wParam, LPARAM lParam)
{
HRESULT hr = S_OK;
LONG evCode, evParam1, evParam2;
while(pME && SUCCEEDED(pME->GetEvent(&evCode, (LONG_PTR*)&evParam1, (LONG_PTR*)&evParam2, 0)))
{
hr = pME->FreeEventParams(evCode, evParam1, evParam2);
if(EC_COMPLETE == evCode)
{
if(m_fRecording)
{
OnRecord();
break;
}
}
}
return hr;
}
void Casf2mkvDlg::OnRecord()
{
UpdateData();
HRESULT hr;
if(!m_fRecording)
{
m_fRecording = true;
UpdateDialogControls(this, FALSE);
hr = E_FAIL;
do
{
// i/o
CString src;
m_combo.GetWindowText(src);
CFileDialog fd(
FALSE, _T("mkv"), m_dst,
OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT|OFN_EXPLORER|OFN_ENABLESIZING,
_T("Matroska file (*.mkv;*.mka)|*.mkv;*.mka||"),
this);
if(fd.DoModal() != IDOK) break;
m_dst = fd.GetPathName();
m_mru.Add(src);
m_mru.WriteList();
SetupCombo();
m_combo.SetWindowText(src);
if(src.Trim().IsEmpty() || m_dst.Trim().IsEmpty())
break;
// filer graph
if(FAILED(hr = pGB.CoCreateInstance(CLSID_FilterGraph)))
break;
pMC = pGB;
pME = pGB;
pMS = pGB;
pVW = pGB;
pBV = pGB;
if(!pMC || !pME || !pMS || !pVW || !pBV)
break;
if(FAILED(hr = pME->SetNotifyWindow((OAHWND)m_hWnd, WM_GRAPHNOTIFY, 0)))
break;
// windows media source filter
CComPtr pSrc;
if(FAILED(hr = pSrc.CoCreateInstance(CLSID_NetShowSource))
|| FAILED(hr = pGB->AddFilter(pSrc, CStringW(src)))
|| FAILED(hr = CComQIPtr(pSrc)->Load(CStringW(src), NULL)))
break;
// matroska muxer
CComPtr pMux;
if(FAILED(hr = pMux.CoCreateInstance(__uuidof(CMatroskaMuxerFilter)))
&& !(pMux = new CMatroskaMuxerFilter(NULL, NULL))
|| FAILED(hr = pGB->AddFilter(pMux, L"Matroska Muxer")))
break;
BeginEnumPins(pSrc, pEP, pPin)
if(FAILED(hr = pGB->Connect(pPin, GetFirstDisconnectedPin(pMux, PINDIR_INPUT))))
break;
EndEnumPins
if(FAILED(hr))
break;
// file writer
CComPtr pFW;
if(FAILED(hr = pFW.CoCreateInstance(CLSID_FileWriter))
|| FAILED(hr = pGB->AddFilter(pFW, CStringW(m_dst)))
|| FAILED(hr = CComQIPtr(pFW)->SetFileName(CStringW(m_dst), NULL))
|| FAILED(hr = CComQIPtr(pFW)->SetMode(AM_FILE_OVERWRITE))
|| FAILED(hr = pGB->Connect(GetFirstDisconnectedPin(pMux, PINDIR_OUTPUT), GetFirstDisconnectedPin(pFW, PINDIR_INPUT))))
break;
// insert inf. pin tee
BeginEnumPins(pMux, pEP, pPin)
{
PIN_DIRECTION dir;
CComPtr pPinTo;
CMediaType mt;
if(FAILED(pPin->QueryDirection(&dir)) || dir != PINDIR_INPUT
|| FAILED(pPin->ConnectedTo(&pPinTo))
|| FAILED(pPin->ConnectionMediaType(&mt)))
continue;
// FIXME: the inf pin tee filter makes the video messed up, like when seeking
// onto a non-keyframe and starting to decode from that point. (audio seems to be ok)
CComPtr pInfPinTee;
if(mt.majortype == MEDIATYPE_Video
|| mt.majortype == MEDIATYPE_Audio)
{
if(FAILED(pGB->Disconnect(pPin))
|| FAILED(pGB->Disconnect(pPinTo))
|| FAILED(pInfPinTee.CoCreateInstance(CLSID_InfTee))
|| FAILED(pGB->AddFilter(pInfPinTee, L"Infinite Pin Tee")))
continue;
if(FAILED(pGB->Connect(pPinTo, GetFirstDisconnectedPin(pInfPinTee, PINDIR_INPUT)))
|| FAILED(pGB->Connect(GetFirstDisconnectedPin(pInfPinTee, PINDIR_OUTPUT), pPin)))
{
pGB->RemoveFilter(pInfPinTee);
pGB->ConnectDirect(pPinTo, pPin, NULL);
continue;
}
}
pPin = GetFirstDisconnectedPin(pInfPinTee, PINDIR_OUTPUT);
CComPtr pRenderer;
if(mt.majortype == MEDIATYPE_Video)
{
if(SUCCEEDED(pRenderer.CoCreateInstance(CLSID_VideoRendererDefault))
|| SUCCEEDED(pRenderer.CoCreateInstance(CLSID_VideoRenderer)))
pPinTo = ::GetFirstDisconnectedPin(pRenderer, PINDIR_INPUT);
}
else if(mt.majortype == MEDIATYPE_Audio)
{
if(SUCCEEDED(pRenderer.CoCreateInstance(CLSID_DSoundRender)))
pPinTo = ::GetFirstDisconnectedPin(pRenderer, PINDIR_INPUT);
}
if(pPin && pPinTo && pRenderer)
{
if(SUCCEEDED(pGB->AddFilter(pRenderer, L"Renderer"))
&& FAILED(pGB->Connect(pPin, pPinTo)))
pGB->RemoveFilter(pRenderer);
}
}
EndEnumPins
// setup video window
if(SUCCEEDED(pVW->put_Owner((OAHWND)m_video.m_hWnd))
&& SUCCEEDED(pVW->put_WindowStyle(WS_CHILD|WS_CLIPSIBLINGS|WS_CLIPCHILDREN))
&& SUCCEEDED(pVW->put_MessageDrain((OAHWND)m_hWnd)))
{
SetVideoRect();
}
// timer for polling the position
SetTimer(1, 500, NULL);
// go!
if(FAILED(hr = pMC->Run()))
break;
hr = S_OK;
}
while(false);
if(FAILED(hr)) OnRecord();
}
else
{
if(pMC) pMC->Stop();
pMC.Release();
pME.Release();
pMS.Release();
pVW.Release();
pBV.Release();
pGB.Release();
m_fRecording = false;
SetWindowText(ResStr(IDS_TITLE));
}
}
void Casf2mkvDlg::OnUpdateRecord(CCmdUI* pCmdUI)
{
CString url;
m_combo.GetWindowText(url);
url.Trim();
pCmdUI->Enable(!url.IsEmpty());
pCmdUI->SetText(ResStr(!m_fRecording ? IDS_RECORD : IDS_STOP));
}
void Casf2mkvDlg::OnUpdateFileName(CCmdUI* pCmdUI)
{
pCmdUI->Enable(!m_fRecording);
}
void Casf2mkvDlg::OnUpdateSettings(CCmdUI* pCmdUI)
{
pCmdUI->Enable(!m_fRecording);
}
void Casf2mkvDlg::OnSize(UINT nType, int cx, int cy)
{
CResizableDialog::OnSize(nType, cx, cy);
SetVideoRect();
}
void Casf2mkvDlg::OnTimer(UINT nIDEvent)
{
if(nIDEvent == 1)
{
if(pMS)
{
REFERENCE_TIME rtPos = 0, rtDur = 0;
pMS->GetCurrentPosition(&rtPos);
pMS->GetDuration(&rtDur);
CString title;
if(rtDur > 0) title.Format(_T("%s (progress: %I64d%%)"), ResStr(IDS_TITLE), 100i64 * rtPos / rtDur);
else title = ResStr(IDS_TITLE) + _T(" (live)");
BeginEnumFilters(pGB, pEF, pBF)
{
if(CComQIPtr pAMNS = pBF)
{
long BufferingProgress = 0;
if(SUCCEEDED(pAMNS->get_BufferingProgress(&BufferingProgress)) && BufferingProgress > 0)
{
CString str;
str.Format(_T(" (buffer: %d%%)"), BufferingProgress);
title += str;
}
break;
}
}
EndEnumFilters
SetWindowText(title);
}
}
CResizableDialog::OnTimer(nIDEvent);
}
void Casf2mkvDlg::OnBnClickedButton2()
{
CComPtr pBF;
pBF.CoCreateInstance(CLSID_NetShowSource);
if(pBF) ShowPPage(pBF, m_hWnd);
}
LRESULT Casf2mkvDlg::OnUrlOpen(WPARAM wParam, LPARAM lParam)
{
m_combo.SetWindowText(CString((char*)lParam));
return 0;
}
//////////////////
Casf2mkvDlg::CRecentFileAndURLList::CRecentFileAndURLList(UINT nStart, LPCTSTR lpszSection,
LPCTSTR lpszEntryFormat, int nSize,
int nMaxDispLen)
: CRecentFileList(nStart, lpszSection, lpszEntryFormat, nSize, nMaxDispLen)
{
}
//#include
extern BOOL AFXAPI AfxFullPath(LPTSTR lpszPathOut, LPCTSTR lpszFileIn);
extern BOOL AFXAPI AfxComparePath(LPCTSTR lpszPath1, LPCTSTR lpszPath2);
void Casf2mkvDlg::CRecentFileAndURLList::Add(LPCTSTR lpszPathName)
{
ASSERT(m_arrNames != NULL);
ASSERT(lpszPathName != NULL);
ASSERT(AfxIsValidString(lpszPathName));
if(CString(lpszPathName).MakeLower().Find(_T("@device:")) >= 0)
return;
bool fURL = (CString(lpszPathName).Find(_T("://")) >= 0);
// fully qualify the path name
TCHAR szTemp[_MAX_PATH];
if(fURL) _tcscpy(szTemp, lpszPathName);
else AfxFullPath(szTemp, lpszPathName);
// update the MRU list, if an existing MRU string matches file name
int iMRU;
for (iMRU = 0; iMRU < m_nSize-1; iMRU++)
{
if((fURL && !_tcscmp(m_arrNames[iMRU], szTemp))
|| AfxComparePath(m_arrNames[iMRU], szTemp))
break; // iMRU will point to matching entry
}
// move MRU strings before this one down
for (; iMRU > 0; iMRU--)
{
ASSERT(iMRU > 0);
ASSERT(iMRU < m_nSize);
m_arrNames[iMRU] = m_arrNames[iMRU-1];
}
// place this one at the beginning
m_arrNames[0] = szTemp;
}