Hi! I've been playing around with using MPlayer to display video in a Max GUI for a project I'm working on. This mod and frontend for MPlayer are the result so far. I don't want to mess around with the front end any more (mainly wrote it for testing) so I thought I'd post it in case anyone else is interested in playing around with it. It will play pretty much any media file you can throw at it as well as streaming content. Also, I haven't used anything Win32 specific so I think it should work on OSX and Linux provided the proper mplayer executable. Here's the mod:
Frontend:
There are a couple requirements to get it working. You'll need Fabian's handy pub.ProcessStream mod available here:
http://www.blitzmax.com/codearcs/codearcs.php?code=1558
And, of course, you'll need MPlayer available here:
http://ftp5.mplayerhq.hu/mplayer/releases/win32/
Wherever you end up placing the MPlayer executable, make sure you also have the sub folder that contains it's config file and OSD font or it acts strangely.
SuperStrict Module video.mplayer Import BRL.Hook Import BRL.Stream Const MP_MSG_DONE:String="DONE" Const MP_MSG_POSITION:String="POSITION" Const MP_MSG_LENGTH:String="LENGTH" Const MP_MSG_SIZE:String="SIZE" Const MP_MSG_DEBUG:String="DEBUG" Type TMPlayer Global mplayerpath:String="c:\mplayer\mplayer.exe" Global HookID:Int=AllocHookId() Field stream:TStream Field debugLvl:Byte=2 Field debugMsg:String Field Filename:String Field Demuxer:String Field Video_Format:String Field Video_Bitrate:Int Field Video_Width:Int Field Video_Height:Int Field Video_FPS:Float Field Video_Aspect:Float Field Audio_Codec:String Field Audio_Format:Int Field Audio_Bitrate:Int Field Audio_Rate:Int Field Audio_NCH:Int Field Length:Float Field TimePos:Float Function Create:TMPlayer(url:String, hwnd:Int, ovlcolor:String) Local this:TMPlayer=New TMPlayer Local cmd:String="execute::"+TMPlayer.mplayerpath cmd:+ " -identify -slave -colorkey "+ovlcolor+" -wid "+hwnd cmd:+ " ~q"+url+"~q" ' this.Debug("Command Line: "+cmd) this.stream=OpenStream(cmd) If this.stream Then Return this Return Null End Function Method Close() WriteCommand("quit") If stream Then stream.close() End Method Method Pause() WriteCommand("pause") End Method Method Stop() WriteCommand("seek 0 1") WriteCommand("pause") End Method Method SetVolume(value:Int) WriteCommand("volume "+value+" 1") End Method Method VolumeUp(amount:Int) WriteCommand("volume +"+amount) End Method Method VolumeDown(amount:Int) WriteCommand("volume -"+amount) End Method Method GetPosTime() WriteCommand("get_time_pos") End Method Method SetPositionTime(time:Float) WriteCommand("seek "+time+" 2") End Method Method SetPosition(val:Int) WriteCommand("seek "+val+" 1") End Method Method SetOSD(val:Int) WriteCommand("osd "+val) End Method Method PrintOSD(txt:String) WriteCommand("osd_show_text "+txt) End Method Method Width:Int() Return Video_Width End Method Method Height:Int() Return Video_Height End Method Method SetDebugLevel(val:Int=1) debugLvl=val End Method Method GetDebugMsg:String() If debugMsg <> "" Then Return debugMsg debugMsg="" End Method Method Update:Int() If stream=Null Then Return 1 While StreamSize(stream) Local line:String=ReadLine(stream) If line[..10]="Exiting..." CloseStream(stream) stream=Null RunHooks(HookID, MP_MSG_DONE) Return 0 EndIf Select line[..(line.find("_"))] Case "ID" Select line[..(line.find("="))] Case "ID_FILENAME" Filename=line[(line.find("=")+1)..] Case "ID_DEMUXER" Demuxer=line[(line.find("=")+1)..] Case "ID_DEMUXER" Demuxer=line[(line.find("=")+1)..] Case "ID_VIDEO_FORMAT" Video_Format=line[(line.find("=")+1)..] Case "ID_VIDEO_BITRATE" Video_Bitrate=Int(line[(line.find("=")+1)..]) Case "ID_VIDEO_WIDTH" Video_Width=Int(line[(line.find("=")+1)..]) If Video_Height Then RunHooks(HookID, MP_MSG_SIZE) Case "ID_VIDEO_HEIGHT" Video_Height=Int(line[(line.find("=")+1)..]) If Video_Width Then RunHooks(HookID, MP_MSG_SIZE) Case "ID_VIDEO_FPS" Video_FPS=Float(line[(line.find("=")+1)..]) Case "ID_VIDEO_ASPECT" Video_Aspect=Float(line[(line.find("=")+1)..]) Case "ID_AUDIO_CODEC" Audio_Codec=line[(line.find("=")+1)..] Case "ID_AUDIO_FORMAT" Audio_Format=Int(line[(line.find("=")+1)..]) Case "ID_AUDIO_BITRATE" Audio_Bitrate=Int(line[(line.find("=")+1)..]) Case "ID_AUDIO_RATE" Audio_Rate=Int(line[(line.find("=")+1)..]) Case "ID_AUDIO_NCH" Audio_NCH=Int(line[(line.find("=")+1)..]) Case "ID_LENGTH" Length=Float(line[(line.find("=")+1)..]) RunHooks(HookID, MP_MSG_LENGTH) End Select Debug(line) Case "ANS" Select line[..(line.find("="))] Case "ANS_TIME_POSITION" TimePos=Float(line[(line.find("=")+1)..]) RunHooks(HookID, MP_MSG_POSITION) End Select Debug("Answer: "+line,2) Default Debug(line) End Select Wend Return 0 End Method Method WriteCommand:Int(cmd:String) If stream=Null Then Return 1 For Local i:Int=0 Until cmd.Length WriteByte(stream, cmd[i]) Next WriteByte(stream, 10) Debug("Command: "+cmd,2) Return 0 End Method Method Debug(msg:String, level:Int=1) If level <= debugLvl ' Print(msg) debugMsg=msg RunHooks(HookID, MP_MSG_DEBUG) EndIf End Method End Type
Frontend:
SuperStrict Framework BRL.EventQueue Import BRL.Win32MaxGUI Import BRL.Timer Import pub.processstream Import video.mplayer Local mp:TMaxPlayer=TMaxPlayer.Create() While mp.Update() WaitEvent Wend End ' File Menu Const MNU_PLYFILE:Int=1 Const MNU_PLYURL:Int=2 Const MNU_QUIT:Int=10 ' View Menu Const MNU_ORIG:Int=200 Const MNU_2X:Int=210 Const MNU_3X:Int=220 Const MNU_FULL:Int=230 ' Options Menu Const MNU_SETTINGS:Int=300 Const MNU_LOG:Int=310 ' Help Menu Const MNU_ABOUT:Int=400 ' OSD Menu Const MNU_OSD_OFF:Int=500 Const MNU_OSD_LVL2:Int=510 Const MNU_OSD_LVL3:Int=520 ' Play File Extensions Const PLAY_EXTENSIONS:String="Video Files:avi,mpg,mpeg,wmv,asf,wma,mov,mp4;All Files:*" Const MP_VOLRANGE:Int=15 Type TMaxPlayer Field mplayer:TMPlayer Field win:TGadget Field pnlVideo:TGadget Field pnlControl:TGadget Field pnlSlider:TGadget Field pnlSliderBar:TGadget Field btnPlay:TGadget Field btnStop:TGadget Field sldVolume:TGadget Field winLog:TGadget Field txaLog:TGadget Field winURL:TGadget Field txtURL:TGadget Field btnURLOK:TGadget Field btnURLCancel:TGadget Field mnuFile:TGadget Field mnuView:TGadget Field mnuOptions:TGadget Field mnuHelp:TGadget Field mnuOSD:TGadget Field symfont:TGuiFont Field updateTimer:TTimer Field positionTimer:TTimer Field paused:Byte Field sliding:Int Field videoLength:Float Field videoPos:Float Function Create:TMaxPlayer() Local this:TMaxPlayer=New TMaxPlayer TMPlayer.mplayerpath=CurrentDir()+"/mplayer.exe" Print TMPlayer.mplayerpath AddHook(TMPlayer.HookID, this.Hook, this) this.symfont=LoadGuiFont("Webdings", 8) this.updateTimer=CreateTimer(16) this.positionTimer=CreateTimer(2) this.win=CreateWindow("MaxPlayer", (ClientWidth(Desktop())-400)/2, (ClientHeight(Desktop())-300)/2, 400, 300, Null, WINDOW_TITLEBAR|WINDOW_RESIZABLE|WINDOW_MENU) SetMinWindowSize(this.win, 180, 180) this.mnuFile=CreateMenu("&File", 0, WindowMenu(this.win)) CreateMenu("Play &File", MNU_PLYFILE, this.mnuFile, KEY_F,MODIFIER_COMMAND) CreateMenu("Play &URL", MNU_PLYURL, this.mnuFile, KEY_U,MODIFIER_COMMAND) CreateMenu("&Quit", MNU_QUIT, this.mnuFile, KEY_Q, MODIFIER_COMMAND) this.mnuView=CreateMenu("&View", 0, WindowMenu(this.win)) DisableGadget(this.mnuView) CreateMenu("&Original Size", MNU_ORIG, this.mnuView, KEY_O,MODIFIER_COMMAND) CreateMenu("&2X Size", MNU_2X, this.mnuView,KEY_2, MODIFIER_COMMAND) CreateMenu("&3X Size", MNU_3X, this.mnuView,KEY_3, MODIFIER_COMMAND) CreateMenu("Full &Screen", MNU_FULL, this.mnuView, KEY_S,MODIFIER_COMMAND) this.mnuOSD=CreateMenu("OSD Level", 0, this.mnuView) CreateMenu("Off", MNU_OSD_OFF, this.mnuOSD) CreateMenu("Time", MNU_OSD_LVL2, this.mnuOSD) CreateMenu("Time/Total", MNU_OSD_LVL3, this.mnuOSD) this.mnuOptions=CreateMenu("Options", 0, WindowMenu(this.win)) CreateMenu("Settings", MNU_SETTINGS, this.mnuOptions, KEY_T, MODIFIER_COMMAND) CreateMenu("View &Log", MNU_LOG, this.mnuOptions, KEY_L, MODIFIER_COMMAND) this.mnuHelp=CreateMenu("Help", 0, WindowMenu(this.win)) CreateMenu("&About", MNU_SETTINGS, this.mnuHelp, KEY_A, MODIFIER_COMMAND) UpdateWindowMenu(this.win) this.pnlVideo=CreatePanel(0, 0, ClientWidth(this.win), ClientHeight(this.win)-30, this.win) ' this.pnlVideo=CreateCanvas(0, 0, ClientWidth(this.win), ClientHeight(this.win)-32, this.win) SetGadgetLayout(this.pnlVideo, EDGE_ALIGNED, EDGE_ALIGNED, EDGE_ALIGNED, EDGE_ALIGNED) SetPanelColor(this.pnlVideo, 10, 10, 10) ' SetGraphics(CanvasGraphics(this.pnlVideo)) ' SetClsColor(10, 10, 10) this.pnlControl=CreatePanel(0, ClientHeight(this.win)-30, ClientWidth(this.win), 30, this.win) SetGadgetLayout(this.pnlControl, EDGE_ALIGNED, EDGE_ALIGNED, EDGE_CENTERED, EDGE_ALIGNED) SetPanelColor(this.pnlControl, 180, 180, 180) DisableGadget(this.pnlControl) this.btnPlay=CreateButton("P", 0, 0, 30, 30, this.pnlControl) SetGadgetLayout(this.btnPlay, EDGE_ALIGNED, EDGE_CENTERED, EDGE_ALIGNED, EDGE_ALIGNED) SetGadgetFont(this.btnPlay, this.symfont) this.btnStop=CreateButton("S", 30, 0, 30, 30, this.pnlControl) SetGadgetLayout(this.btnSTOP, EDGE_ALIGNED, EDGE_CENTERED, EDGE_ALIGNED, EDGE_ALIGNED) this.pnlSlider=CreatePanel(62, 2, ClientWidth(this.win)-124, 26, this.pnlControl, PANEL_BORDER|PANEL_ACTIVE) SetGadgetLayout(this.pnlSlider, EDGE_ALIGNED, EDGE_ALIGNED, EDGE_ALIGNED, EDGE_ALIGNED) SetPanelColor(this.pnlSlider, 0, 0, 0) this.pnlSliderBar=CreatePanel(0, 0, 1, ClientHeight(this.pnlSlider), this.pnlSlider, PANEL_ACTIVE) SetGadgetLayout(this.pnlSliderBar, EDGE_ALIGNED, EDGE_RELATIVE, EDGE_ALIGNED, EDGE_ALIGNED) SetPanelColor(this.pnlSliderBar, 0, 255, 0) this.sldVolume=CreateSlider(ClientWidth(this.win)-60, 0, 60, 30, this.pnlControl, SLIDER_HORIZONTAL|SLIDER_TRACKBAR) SetGadgetLayout(this.sldVolume, EDGE_CENTERED, EDGE_ALIGNED, EDGE_ALIGNED, EDGE_ALIGNED) SetSliderRange(this.sldVolume, 0, MP_VOLRANGE) SetSliderValue(this.sldVolume, MP_VOLRANGE) this.winLog=CreateWindow("MPlayer Log", 0, 0, 500, 600, Null, WINDOW_TITLEBAR|WINDOW_RESIZABLE|WINDOW_HIDDEN) this.txaLog=CreateTextArea(0, 0, ClientWidth(this.winLog), ClientHeight(this.winLog), this.winLog, TEXTAREA_READONLY) SetGadgetLayout(this.txaLog, EDGE_ALIGNED, EDGE_ALIGNED, EDGE_ALIGNED, EDGE_ALIGNED) this.winURL=CreateWindow("Enter URL", (ClientWidth(Desktop())-500)/2, (ClientHeight(Desktop())-112)/2, 500, 112, Null, WINDOW_TITLEBAR|WINDOW_HIDDEN) this.txtURL=CreateTextField(10, 10, ClientWidth(this.winURL)-20, 20, this.winURL) this.btnURLOK=CreateButton("OK", 10, 42, 60, 30, this.winURL) this.btnURLCancel=CreateButton("Cancel", ClientWidth(this.winURL)-70, 42, 60, 30, this.winURL) Return this End Function Method Close() If mplayer Then mplayer.Close() mplayer=Null SetSliderValue(sldVolume, MP_VOLRANGE) DisableGadget(pnlControl) DisableGadget(mnuView) UpdateWindowMenu(win) SetSlider(0) Delay 300 End Method Function Hook:Object(id:Int, data:Object, context:Object) Local mp:TMaxPlayer=TMaxPlayer(context) Select String(data) Case MP_MSG_DONE mp.Close() Case MP_MSG_POSITION mp.VideoPos=mp.mplayer.TimePos Case MP_MSG_LENGTH mp.VideoLength=mp.mplayer.Length Case MP_MSG_SIZE mp.SetVideoSize() Case MP_MSG_DEBUG AddTextAreaText(mp.txaLog, mp.mplayer.GetDebugMsg()+"~n") End Select Return data End Function Method Update:Byte() ' Cls ' Flip ' If CurrentEvent.ToString()[..5]<>"Timer" Then Print CurrentEvent.ToString() Select EventID() Case EVENT_WINDOWCLOSE Select EventSource() Case win Close() Return False Case winLog HideGadget(winLog) Case winURL SetGadgetText(txtURL,"") HideGadget(winURL) EnableGadget(win) End Select Case EVENT_TIMERTICK Select EventSource() Case updateTimer If mplayer Then mplayer.Update() Case positionTimer If mplayer And Not paused And Not sliding mplayer.GetPosTime() SetSlider((videoPos*ClientWidth(pnlSlider))/videoLength) EndIf End Select Case EVENT_GADGETACTION Select EventSource() Case btnPlay If mplayer Then mplayer.Pause() paused=Not paused Case btnStop If mplayer Then mplayer.Stop() SetSlider(0) paused=True Case sldVolume Local val:Int=EventData() If val Then val=(val*100)/MP_VOLRANGE mplayer.SetVolume(val) Case btnURLOK HideGadget(winURL) EnableGadget(win) If mplayer Then Close() PlayFile(TextFieldText(txtURL)) paused=False Case btnURLCancel SetGadgetText(txtURL,"") HideGadget(winURL) EnableGadget(win) End Select Case EVENT_MOUSEDOWN Select EventSource() Case pnlSlider, pnlSliderBar sliding=True SetSlider(EventX()) End Select Case EVENT_MOUSEMOVE Select EventSource() Case pnlSlider, pnlSliderBar If sliding Then SetSlider(EventX()) End Select Case EVENT_MOUSEUP Select EventSource() Case pnlSlider, pnlSliderBar mplayer.SetPosition((EventX()*100)/ClientWidth(pnlSlider)) sliding=False End Select Case EVENT_MENUACTION Select EventData() Case MNU_PLYFILE If mplayer Then Close() PlayFile(RequestFile("Select Media File", PLAY_EXTENSIONS)) paused=False Case MNU_PLYURL DisableGadget(win) ShowGadget(winURL) Case MNU_QUIT Close() Return False Case MNU_ORIG SetVideoSize() mplayer.PrintOSD("Original Size") Case MNU_2X SetVideoSize(2) mplayer.PrintOSD("2X Size") Case MNU_3X SetVideoSize(3) mplayer.PrintOSD("3X Size") Case MNU_OSD_OFF mplayer.SetOSD(1) Case MNU_OSD_LVL2 mplayer.SetOSD(2) Case MNU_OSD_LVL3 mplayer.SetOSD(3) Case MNU_LOG ShowGadget(winLog) End Select End Select Return True End Method Method SetSlider(val:Int) If val < 1 Then val=1 If val > ClientWidth(pnlSlider) Then val=ClientWidth(pnlSlider) Local x:Int=GadgetX(pnlSliderBar) Local y:Int=GadgetY(pnlSliderBar) Local height:Int=GadgetHeight(pnlSliderBar) SetGadgetShape(pnlSliderBar, x, y, val, height) videoPos=(val*videoLength)/ClientWidth(pnlSlider) End Method Method PlayFile(file:String) MPlayer=TMPlayer.Create(file, QueryGadget(pnlVideo, QUERY_HWND), "0x0A0A0A") If mplayer mplayer.SetDebugLevel(1) EnableGadget(pnlControl) EnableGadget(mnuView) UpdateWindowMenu(win) Else Notify("Unable to open file: "+file,True) EndIf End Method Method SetVideoSize(size:Int=1) Local width:Int=(mplayer.Width()*size)+(GadgetWidth(win)-ClientWidth(win)) Local height:Int=(mplayer.Height()*size)+(GadgetHeight(win)-ClientHeight(win))+GadgetHeight(pnlControl) Local x:Int=(ClientWidth(Desktop())-width)/2 Local y:Int=(ClientHeight(Desktop())-height)/2 SetGadgetShape(win, x, y, width, height) End Method End Type
There are a couple requirements to get it working. You'll need Fabian's handy pub.ProcessStream mod available here:
http://www.blitzmax.com/codearcs/codearcs.php?code=1558
And, of course, you'll need MPlayer available here:
http://ftp5.mplayerhq.hu/mplayer/releases/win32/
Wherever you end up placing the MPlayer executable, make sure you also have the sub folder that contains it's config file and OSD font or it acts strangely.