和VLC、Xine不太一样,MPlayer采用控制台的输入输出来和外壳程序打交道。而VLC和Xine则提供了一堆API,相对比较容易一些。于是编 写一个MPlayer的基本的方法,就是重定向MPlayer的标准输入输出到管道,通过读取MPlayer的输出来分析MPlayer的信息和状态,通 过向MPlayer的标准输入发送命令来控制播放。MPlayer的命令行参数相当复杂,需要仔细阅读其文档和代码。
向MPlayer发送的命令可以参考 http://www.mplayerhq.hu/DOCS/tech/slave.txt
MPlayer的man page看这里 http://www.mplayerhq.hu/DOCS/man/en/mplayer.1.html
SMplayer的源代码 https://smplayer.svn.sourceforge.net/svnroot/smplayer
Roger打算把MPlayer嵌入某个软件中,所以也开始写一个MPlayer的外壳程序,还没写多少,很简陋,但是已经可以跑起来了,下面给出控制部分的代码,界面部分是MFC的,就不放出来了。
最开始的想法是问答式的,在需要某个状态时向MPlayer发命令,然后阻塞等待MPlayer返回,这样效率比较低,就改成了现在这样子,有一个后台线程负责接收
Mplayer的输出并进行分析,状态变量都在类实例里面保存一份,这样就不必等待,比较快一点。
在编写中遇到的一些问题:
1. 显示到某个窗口上,-wid 窗口句柄值 -vo directx:noaccel,如果不禁用加速则显示不出来;但是smplayer不禁用也可以,真神奇。
2. -identify可以让MPlayer在开始播放之后显示一堆信息,很方便
3. 似乎没有办法获取音量?于是自己维护了一个,每次启动后设置一下
4. 设置MPlayer的working dir很重要,一方面截屏的图片保存在那里,另外像-vo jpeg:outdir=这样的选项Windows下没法设全路径,也要靠当前目录。
/* MPlayer.h */
#ifndef _MPLAYER_H_
#define _MPLAYER_H_
#include <windows.h>
#include <stdio.h>
#define WM_MFACE (WM_USER+0×101)
#define LOG_MPLAYER (0)
#define LOG_USER (1)
#define LOG_APP (2)
#define VERBOSE_QUIET (3)
#define VERBOSE_ERR (2)
#define VERBOSE_WARN (1)
#define VERBOSE_DBG (0)
#define LOG_DEBUG (0)
#define LOG_WARNING (1)
#define LOG_ERROR (2)
#define STATUS_STOP (0)
#define STATUS_STOPING (1)
#define STATUS_STARTING (2)
#define STATUS_PLAY (3)
#define STATUS_PAUSE (4)
#define DBG(who, msg) Log(who, LOG_DEBUG, msg, __LINE__, __FILE__)
#define WARN(who, msg) Log(who, LOG_WARNING, msg, __LINE__, __FILE__)
#define ERR(who, msg) Log(who, LOG_ERROR, msg, __LINE__, __FILE__)
#define META_MAX_NAME_LEN 31
#define META_MAX_VALUE_LEN 255
#define MAX_META 64
typedef struct _meta
{
char name [META_MAX_NAME_LEN+1];
char value[META_MAX_VALUE_LEN+1];
}meta;
class MPlayer
{
public:
MPlayer ();
~MPlayer();
void SetMPlayerPath (LPCSTR szMplayerPath)
{
strncpy (m_strMPlayerPath, szMplayerPath, MAX_PATH);
}
void SetWorkingPath (LPCSTR szWorkingPath)
{
strncpy (m_strWorkingPath, szWorkingPath, MAX_PATH);
}
void SetMediaPath (LPCSTR szMediaPath)
{
strncpy (m_strMediaPath, szMediaPath, 1024);
}
void SetParam (LPCSTR szParam)
{
strncpy (m_strParam, szParam, 255);
}
void SetNotifyWindow (HANDLE hWnd, DWORD dwMessage = WM_MFACE)
{
m_hNotifyWindow = hWnd;
m_dwMessage = dwMessage;
}
void SetDisplayWindow (HANDLE hWnd)
{
m_hDisplayWindow = hWnd;
}
void SetVo (const char* szVout)
{
strncpy (m_strVout, szVout, 255);
}
void SetAo (const char* szAout)
{
strncpy (m_strAout, szAout, 255);
}
LPCSTR GetParam ()
{
return m_strParam;
}
LPCSTR GetMediaPath ()
{
return m_strMediaPath;
}
double GetFPS ()
{
return m_lfFPS;
}
const char* GetVideoFormat ()
{
return m_strVideoFormat;
}
const char* GetAudioFormat ()
{
return m_strAudioFormat;
}
const char* GetVideoCodec ()
{
return m_strVideoCodec;
}
const char* GetAudioCodec ()
{
return m_strAudioCodec;
}
const char* GetMPlayerVersion ()
{
return m_strVersion;
}
double GetMediaLength ()
{
return m_lfMediaLength;
}
int GetPos ()
{
return m_iPos;
}
int GetVideoWidth ()
{
return m_iVideoWidth;
}
int GetVideoHeight ()
{
return m_iVideoHeight;
}
int SetVerbose (int verbose)
{
if (verbose < VERBOSE_DBG) verbose = VERBOSE_DBG;
if (verbose > VERBOSE_QUIET) verbose = VERBOSE_QUIET;
m_iVerbose = verbose;
return m_iVerbose;
}
int GetMetaCount ()
{
return m_iMetas;
}
const char* GetMetaName (int nIndex)
{
if (nIndex >= 0 && nIndex < m_iMetas)
{
return m_arryMetas[nIndex].name;
}
else
{
return NULL;
}
}
const char* GetMetaValue (int nIndex)
{
if (nIndex >= 0 && nIndex < m_iMetas)
{
return m_arryMetas[nIndex].value;
}
else
{
return NULL;
}
}
int Play ();
int Stop ();
int Pause ();
int Seek (int Pos);
int GetVolume ();
int SetVolume (int Volume);
int SetMute (int Mute);
int GetMute ();
int GetTitle (char* buf, int nBuf);
int GetAlbum (char* buf, int nBuf);
int GetArtist (char* buf, int nBuf);
int GetComment (char* buf, int nBuf);
int GetGenre (char* buf, int nBuf);
int GetStatus () { return m_iStatus; }
int FullScreen ();
int Snapshot ();
const char* GetMeta (const char* szName);
int CreateSnapshots (LPCSTR szDir, int nInterval = 60, int nCount = 0);
private:
int CreateMPlayerProcess();
int SendCommand (LPCSTR szCommand);
void Log (int who, int level, LPCSTR message, int line, const char* file);
void KillMPlayer ();
void Notify (int param);
void ResetStatus ();
int GetMPlayerMessages ();
void ProcessMessage (LPCSTR szMsg);
const char* hasString (const char* szMsg, const char* style);
static DWORD WINAPI WorkThread (PVOID pThis);
DWORD WorkThreadFunc ();
// config
char m_strMPlayerPath [MAX_PATH+1];
char m_strWorkingPath [MAX_PATH+1];
char m_strMediaPath [1025];
char m_strParam [256];
char m_strVout [256];
char m_strAout [256];
int m_iVerbose;
HANDLE m_hMPlayerProcess;
HANDLE m_hWorkingThread;
HANDLE m_hPipeRead;
HANDLE m_hPipeWrite;
HANDLE m_hNotifyWindow;
HANDLE m_hDisplayWindow;
DWORD m_dwMessage;
// status
double m_lfMediaLength;
int m_iPos;
int m_iVolume;
int m_iVideoWidth;
int m_iVideoHeight;
int m_iAudioBitrate;
double m_lfFPS;
char m_strVideoFormat [16];
char m_strAudioFormat [16];
char m_strVideoCodec [16];
char m_strAudioCodec [16];
char m_strDemuxer [16];
char m_strVersion [128];
bool m_bMute;
int m_iStatus; // TODO: protect this var for multithread readwrite
meta m_arryMetas[MAX_META];
int m_iMetas;
};
#endif //_MPLAYER_H_
/* MPlayer.cpp */
#include “MPlayer.h”
#include <conio.h>
#define ZeroString(str) ZeroMemory (str, sizeof (str))
#define MP_MUST_PLAYING(errCode) /
if (m_iStatus != STATUS_PLAY) { /
WARN (LOG_APP, “MPlayer is not playing!”); /
return (errCode); /
}
MPlayer::MPlayer()
{
strcpy (m_strVout, “directx:noaccel”);
strcpy (m_strAout, “dsound”);
m_dwMessage = WM_MFACE;
m_hMPlayerProcess = INVALID_HANDLE_VALUE;
m_hPipeRead = INVALID_HANDLE_VALUE;
m_hPipeWrite = INVALID_HANDLE_VALUE;
m_hNotifyWindow = INVALID_HANDLE_VALUE;
m_hDisplayWindow = INVALID_HANDLE_VALUE;
m_hWorkingThread = INVALID_HANDLE_VALUE;
m_iVolume = 100;
m_iVerbose = VERBOSE_ERR;
ZeroString (m_strParam);
ZeroString (m_strMediaPath);
ZeroString (m_strWorkingPath);
ResetStatus ();
DBG (LOG_APP, “MPlayer object created”);
}
MPlayer::~MPlayer()
{
if (m_iStatus != STATUS_STOP)
{
Stop ();
}
}
void MPlayer::ResetStatus ()
{
m_iStatus = STATUS_STOP;
m_bMute = false;
m_lfMediaLength = 0.0;
m_iVideoWidth = 0;
m_iVideoHeight = 0;
m_iPos = 0;
m_iAudioBitrate = 0;
m_lfFPS = 0.0;
m_iMetas = 0;
ZeroString (m_strVideoFormat);
ZeroString (m_strAudioFormat);
ZeroString (m_strDemuxer);
ZeroString (m_strVideoCodec);
ZeroString (m_strAudioCodec);
ZeroString (m_strVersion);
ZeroString (m_arryMetas);
}
int MPlayer::Play ()
{
int ret;
if (m_iStatus != STATUS_STOP)
{
Stop ();
}
m_iStatus = STATUS_STARTING;
ret = CreateMPlayerProcess ();
if (ret)
{
m_iStatus = STATUS_STOP;
return ret;
}
m_hWorkingThread = CreateThread (0, 0, MPlayer::WorkThread, (PVOID)this, 0, 0);
if (!m_hWorkingThread)
{
ERR (LOG_APP, “Failed to create working thread!”);
Stop ();
return 1;
}
return 0;
}
int MPlayer::Stop ()
{
if (m_iStatus != STATUS_STOP)
{
m_iStatus = STATUS_STOPING;
Sleep (300); /* wait for work thread quit*/
SendCommand (”quit”);
KillMPlayer ();
}
else
{
WARN (LOG_APP, “Already stopped”);
}
return 0;
}
int MPlayer::Pause ()
{
if (m_iStatus == STATUS_PLAY)
{
DBG (LOG_APP, “Pause”);
SendCommand (”pause”);
m_iStatus = STATUS_PAUSE;
}
else if (m_iStatus == STATUS_PAUSE)
{
DBG (LOG_APP, “Continue play”);
SendCommand (”pause”);
m_iStatus = STATUS_PLAY;
}
return 0;
}
int MPlayer::FullScreen ()
{
MP_MUST_PLAYING (1);
SendCommand (”vo_fullscreen”);
return 0;
}
int MPlayer::GetVolume ()
{
MP_MUST_PLAYING (0);
return m_iVolume;
}
int MPlayer::SetVolume (int Vol)
{
MP_MUST_PLAYING (1);
char buf[256];
if (Vol > 100) Vol = 100;
if (Vol < 0) Vol = 0;
m_iVolume = Vol;
sprintf (buf, “volume %d 1″, Vol);
SendCommand (buf);
m_bMute = false;
return 0;
}
int MPlayer::GetMute ()
{
MP_MUST_PLAYING (0);
return m_bMute ? 1 : 0;
}
int MPlayer::SetMute(int Mute)
{
MP_MUST_PLAYING (1);
char buf[256];
sprintf (buf, “mute %d”, Mute?1:0);
SendCommand (buf);
m_bMute = Mute ? true : false;
return 0;
}
int MPlayer::Snapshot ()
{
MP_MUST_PLAYING (1);
SendCommand (”screenshot 0″);
return 0;
}
int MPlayer::GetTitle (char* buf, int nBuf)
{
MP_MUST_PLAYING (1);
return 1;
}
int MPlayer::GetAlbum (char* buf, int nBuf)
{
MP_MUST_PLAYING (1);
return 1;
}
int MPlayer::GetArtist (char* buf, int nBuf)
{
MP_MUST_PLAYING (1);
return 1;
}
int MPlayer::GetComment (char* buf, int nBuf)
{
MP_MUST_PLAYING (1);
return 1;
}
int MPlayer::GetGenre (char* buf, int nBuf)
{
MP_MUST_PLAYING (1);
return 1;
}
int MPlayer::Seek (int nPos)
{
MP_MUST_PLAYING (1);
char buf[256];
sprintf (buf, “seek %d 2″, nPos);
SendCommand (buf);
return 0;
}
void MPlayer::Log (int who, int level, LPCSTR message, int line, const char* file)
{
char* szWho[3] = {”MPlayer”, “Input”, “App”};
char* szLevel[3] = {”Debug”, “Warning”, “Error”};
if (level >= m_iVerbose)
{
printf (”[%s] %s %s L%d: %s/n”, szWho[who%3], szLevel[level%3], file, line, message);
}
}
int MPlayer::CreateMPlayerProcess()
{
HANDLE h1, h2;
SECURITY_ATTRIBUTES sa;
PROCESS_INFORMATION pi;
STARTUPINFO si;
char param [1024];
char buf[256];
DBG (LOG_APP, “Creating MPlayer Process”);
strcpy (param, m_strMPlayerPath);
strcat (param, ” /”");
strcat (param, m_strMediaPath);
strcat (param, “/” “);
if (m_hDisplayWindow != INVALID_HANDLE_VALUE)
{
sprintf (buf, “-wid %d”, (int)m_hDisplayWindow);
strcat (param, buf);
strcat (param, ” “);
}
sprintf (buf, “-vo %s -slave -quiet -identify -vf screenshot -ao %s”, m_strVout, m_strAout);
strcat (param, buf);
strcat (param, ” “);
strcat (param, m_strParam);
ZeroMemory (&sa, sizeof(sa));
sa.nLength = sizeof (sa);
sa.bInheritHandle = TRUE;
if (!CreatePipe (&m_hPipeRead, &h1, &sa,0))
{
ERR (LOG_APP, “Error createing pipe!”);
return GetLastError();
}
if (!CreatePipe (&h2, &m_hPipeWrite, &sa, 0))
{
ERR (LOG_APP, “Error createing pipe!”);
return GetLastError();
}
ZeroMemory (&si, sizeof(si));
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.wShowWindow = SW_HIDE;
si.hStdInput = h2;
si.hStdOutput = si.hStdError = h1;
ZeroMemory (&pi, sizeof(pi));
if (!CreateProcess (m_strMPlayerPath, param, 0, 0, TRUE, 0, 0,
*m_strWorkingPath ? m_strWorkingPath : NULL, &si, &pi))
{
return GetLastError();
}
CloseHandle (h1);
CloseHandle (h2);
CloseHandle (pi.hThread);
m_hMPlayerProcess = pi.hProcess;
DBG (LOG_APP, “MPlayer Started!”);
return 0;
}
int MPlayer::CreateSnapshots (LPCSTR szDir, int nInterval, int nCount)
{
PROCESS_INFORMATION pi;
STARTUPINFO si;
char param [1024];
char buf[256];
char curdir[256];
strcpy (param, m_strMPlayerPath);
strcpy (curdir, m_strMPlayerPath);
if(strrchr (curdir, ‘//’))
{
*strrchr (curdir, ‘//’) = 0;
}
strcat (param, ” /”");
strcat (param, m_strMediaPath);
strcat (param, “/” “);
sprintf (buf, “-ao null -framedrop -frames %d -sstep %d -vo jpeg:outdir=/”%s/” “,
nCount, nInterval, szDir);
strcat (param, buf);
ZeroMemory (&si, sizeof(si));
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
ZeroMemory (&pi, sizeof(pi));
if (!CreateProcess (m_strMPlayerPath, param,0,0,TRUE,0,0,curdir,&si,&pi))
{
return GetLastError();
}
DBG (LOG_APP, “MPlayer Started!”);
CloseHandle (pi.hThread);
if (WAIT_TIMEOUT == WaitForSingleObject (pi.hProcess, 20000))
{
DBG (LOG_APP, “Timeout …Terminate MPlayer!”);
TerminateProcess (pi.hProcess, 0);
CloseHandle (pi.hProcess);
return 1;
}
CloseHandle (pi.hProcess);
DBG (LOG_APP, “Finish Snapshots!”);
return 0;
}
int MPlayer::SendCommand (LPCSTR szCommand)
{
DWORD dwWriten;
char buf[512];
if (m_iStatus == STATUS_STOP)
{
WARN (LOG_APP, “Send command when mplayer is stopped”);
return 1;
}
sprintf (buf, “%s/n”, szCommand);
DBG (LOG_USER, buf);
if (!WriteFile (m_hPipeWrite, buf, strlen (buf), &dwWriten, NULL))
{
ERR (LOG_APP, “Error Writing Pipe!”);
KillMPlayer ();
Notify (1);
return 1;
}
return 0;
}
void MPlayer::KillMPlayer ()
{
int delay = 1000;
DWORD ret;
DBG (LOG_APP, “Try to stop mplayer”);
ret = WaitForSingleObject (m_hMPlayerProcess, delay);
if (ret == WAIT_TIMEOUT)
{
DBG (LOG_APP, “Terminate mplayer process”);
TerminateProcess (m_hMPlayerProcess, 0);
}
DBG (LOG_APP, “mplayer stopped”);
// Don’t kill myself
// DBG (LOG_APP, “Try to stop working thread”);
// ret = WaitForSingleObject (m_hWorkingThread, delay);
// if (ret == WAIT_TIMEOUT)
// {
// DBG (LOG_APP, “Terminate working thread”);
// TerminateThread (m_hWorkingThread, 0);
// }
// DBG (LOG_APP, “Working thread stopped”);
CloseHandle (m_hMPlayerProcess);
CloseHandle (m_hPipeRead);
CloseHandle (m_hPipeWrite);
ResetStatus ();
}
DWORD WINAPI MPlayer::WorkThread (PVOID pThis)
{
// just convert static member function to non-static function
MPlayer* self = (MPlayer*)pThis;
return self->WorkThreadFunc ();
}
DWORD MPlayer::WorkThreadFunc ()
{
DBG (LOG_APP, “Work thread start”);
const int bufSize = 4096;
const char* pMessage;
char buf[bufSize + 1] = {0};
const char seps[] = “/r/n”;
DWORD ret = 0, dwPeeked = 0, dwRead = 0;
DWORD dwTime = 0;
while (m_iStatus != STATUS_STOPING)
{
/* Update current position */
if (GetTickCount() - dwTime > 500)
{
SendCommand (”get_time_pos”);
dwTime = GetTickCount ();
}
ret = PeekNamedPipe (m_hPipeRead, NULL, 0, 0, &dwPeeked, 0);
if (!ret)
{
if (m_iStatus == STATUS_STOPING)
{
//Stopping, simply quit
break;
}
ERR (LOG_APP, “Error peeking pipe!”);
KillMPlayer ();
Notify (1);
break;
}
if(dwPeeked)
{
if (dwPeeked > bufSize)
{
dwPeeked = bufSize;
}
ret = ReadFile (m_hPipeRead, buf, dwPeeked, &dwRead, 0);
if(!ret)
{
if (m_iStatus == STATUS_STOPING)
{
//Stopping, simply quit
break;
}
ERR (LOG_APP, “Error reading pipe!”);
KillMPlayer ();
Notify (1);
break;
}
buf [dwRead] = 0;
// TODO: consider a message not in one buffer
pMessage = strtok (buf, seps );
while (pMessage != NULL)
{
ProcessMessage (pMessage);
pMessage = strtok( NULL, seps );
}
continue;
}
else
{
Sleep (100);
}
}
DBG (LOG_APP, “Work thread about to quit”);
return 0;
}
const char* MPlayer::hasString (const char* szMsg, const char* style)
{
int n = strlen (style);
if (strnicmp(szMsg, style, n))
{
return NULL;
}
else
{
return szMsg+n;
}
}
void MPlayer::ProcessMessage(LPCSTR szMsg)
{
//DBG (LOG_MPLAYER, szMsg);
const char* pData;
if (pData = hasString (szMsg, “MPlayer “))
{
DBG (LOG_MPLAYER, szMsg);
strncpy (m_strVersion, szMsg, sizeof (m_strVersion) - 1);
return;
}
if (pData = hasString (szMsg, “ID_DEMUXER=”))
{
DBG (LOG_MPLAYER, szMsg);
strncpy (m_strDemuxer, pData, sizeof (m_strDemuxer) - 1);
return;
}
if (pData = hasString (szMsg, “ID_VIDEO_FORMAT=”))
{
DBG (LOG_MPLAYER, szMsg);
strncpy (m_strVideoFormat, pData, sizeof (m_strVideoFormat) - 1);
return;
}
if (pData = hasString (szMsg, “ID_VIDEO_CODEC=”))
{
DBG (LOG_MPLAYER, szMsg);
strncpy (m_strVideoCodec, pData, sizeof (m_strVideoCodec) - 1);
return;
}
if (pData = hasString (szMsg, “ID_AUDIO_FORMAT=”))
{
DBG (LOG_MPLAYER, szMsg);
strncpy (m_strAudioFormat, pData, sizeof (m_strAudioFormat) - 1);
return;
}
if (pData = hasString (szMsg, “ID_AUDIO_CODEC=”))
{
DBG (LOG_MPLAYER, szMsg);
strncpy (m_strAudioCodec, pData, sizeof (m_strAudioCodec) - 1);
return;
}
if (pData = hasString (szMsg, “ID_VIDEO_WIDTH=”))
{
DBG (LOG_MPLAYER, szMsg);
m_iVideoWidth = atoi (pData);
return;
}
if (pData = hasString (szMsg, “ID_VIDEO_HEIGHT=”))
{
DBG (LOG_MPLAYER, szMsg);
m_iVideoHeight = atoi (pData);
return;
}
if (pData = hasString (szMsg, “ID_VIDEO_FPS=”))
{
DBG (LOG_MPLAYER, szMsg);
m_lfFPS = atof (pData);
return;
}
if (pData = hasString (szMsg, “ID_AUDIO_BITRATE=”))
{
DBG (LOG_MPLAYER, szMsg);
m_iAudioBitrate = atoi (pData);
return;
}
if (pData = hasString (szMsg, “ID_LENGTH=”))
{
DBG (LOG_MPLAYER, szMsg);
m_lfMediaLength = atof (pData);
return;
}
if (pData = hasString (szMsg, “Starting playback…”))
{
DBG (LOG_MPLAYER, szMsg);
m_iStatus = STATUS_PLAY;
SetVolume (m_iVolume);
return;
}
if (pData = hasString (szMsg, “ANS_TIME_POSITION=”))
{
DBG (LOG_MPLAYER, szMsg);
m_iPos = atoi (pData);
return;
}
if (pData = hasString (szMsg, “Exiting…”))
{
DBG (LOG_MPLAYER, szMsg);
return;
}
if (pData = hasString (szMsg, “ID_CLIP_INFO_NAME”))
{
DBG (LOG_MPLAYER, szMsg);
int n = atoi (pData);
if (n >= MAX_META)
{
WARN (LOG_APP, “Reach max meta count!”);
return;
}
if (m_iMetas <= n)
{
m_iMetas = n + 1;
}
strncpy (m_arryMetas[n].name, pData+2, META_MAX_NAME_LEN);
return;
}
if (pData = hasString (szMsg, “ID_CLIP_INFO_VALUE”))
{
DBG (LOG_MPLAYER, szMsg);
int n = atoi (pData);
if (n >= MAX_META)
{
WARN (LOG_APP, “Reach max meta count!”);
return;
}
if (m_iMetas <= n)
{
m_iMetas = n + 1;
}
strncpy (m_arryMetas[n].value, pData+2, META_MAX_VALUE_LEN);
return;
}
}
void MPlayer::Notify (int param)
{
if (m_hNotifyWindow != INVALID_HANDLE_VALUE)
{
PostMessage ((HWND)m_hNotifyWindow, m_dwMessage, param, 0);
}
}
/* test code */
int main (int argc, char** argv)
{
MPlayer mp;
mp.SetVerbose(VERBOSE_DBG);
mp.SetMPlayerPath (”D://Program//MPlayer-1.0rc2//MPlayer.exe”);
mp.SetMediaPath (”D://cygwin//1.rmvb”);
mp.Play ();
DWORD t1 = GetTickCount();
while (mp.GetStatus() != STATUS_PLAY) Sleep (10);
printf (”(%d ms) Video [%s] %d x %d/n”, GetTickCount() - t1, mp.GetVideoFormat(), mp.GetVideoWidth(), mp.GetVideoHeight());
mp.SetVerbose(VERBOSE_ERR);
while (!_kbhit())
{
printf (”Playing: %d/%d/r”, mp.GetPos(), (int)mp.GetMediaLength());
Sleep (500);
}
getch ();
return 0;
}