1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        mediactrl_ffmpeg.cpp
3 // Purpose:     Displays video
4 // Author:      Alex Thuering
5 // Created:     21.01.2011
6 // RCS-ID:      $Id: mediactrl_ffmpeg.cpp,v 1.12 2015/08/02 17:31:18 ntalex Exp $
7 // Copyright:   (c) Alex Thuering
8 // Licence:     GPL
9 /////////////////////////////////////////////////////////////////////////////
10 
11 #include "mediactrl_ffmpeg.h"
12 #include <wx/wx.h>
13 #include <wx/dcclient.h>
14 #include <wxSVG/mediadec_ffmpeg.h>
15 
16 using namespace std;
17 
18 class FFEvtHandler: public wxEvtHandler {
19 public:
FFEvtHandler(wxMediaCtrl * ctrl)20 	FFEvtHandler(wxMediaCtrl* ctrl): m_ctrl(ctrl) {
21 		m_videoFormat = vfPAL;
22 		m_aspectRatio = ar4_3;
23 		m_frameAspectRatio = 1;
24 	}
25 
~FFEvtHandler()26 	virtual ~FFEvtHandler() {
27 		// nothing to do
28 	}
29 
GetImage()30 	inline wxImage GetImage() {
31 		return m_image;
32 	}
33 
SetImage(wxImage image)34 	inline void SetImage(wxImage image) {
35 		m_image = image;
36 		m_bitmap = wxBitmap();
37 		m_ctrl->Refresh();
38 	}
39 
SetFrameAspectRatio(float frameAspectRatio)40 	inline void SetFrameAspectRatio(float frameAspectRatio) {
41 		m_frameAspectRatio = frameAspectRatio;
42 	}
43 
SetVideoFormat(VideoFormat videoFormat,AspectRatio aspectRatio,const vector<int> & pad,const vector<int> & crop)44 	inline void SetVideoFormat(VideoFormat videoFormat, AspectRatio aspectRatio,
45 			const vector<int>& pad, const vector<int>& crop) {
46 		m_videoFormat = videoFormat;
47 		m_aspectRatio = aspectRatio;
48 		m_pad = pad;
49 		m_crop = crop;
50 		m_bitmap = wxBitmap();
51 		m_ctrl->Refresh();
52 	}
53 
54 protected:
OnPaint(wxPaintEvent & event)55 	void OnPaint(wxPaintEvent &event) {
56 		wxPaintDC dc(m_ctrl);
57 		if (!m_bitmap.Ok()) {
58 			if (!m_image.Ok())
59 				return;
60 			wxImage img = m_image;
61 			double aspect = m_frameAspectRatio;
62 			if (m_aspectRatio != arAUTO)
63 				aspect = m_aspectRatio == ar4_3 ? 4.0/3 : 16.0/9;
64 			wxSize frameSize = m_videoFormat != vfCOPY ? GetFrameSize(m_videoFormat)
65 					: wxSize(img.GetWidth(), img.GetHeight());
66 			if (m_crop.size() == 4 && m_crop[0] + m_crop[1] + m_crop[2] + m_crop[3] > 0) {
67 				int w = img.GetWidth() - m_crop[0] - m_crop[1];
68 				int h = img.GetHeight() - m_crop[2] - m_crop[3];
69 				img = img.GetSubImage(wxRect(m_crop[0], m_crop[2], w > 0 ? w : 0, h > 0 ? h : 0));
70 			}
71 			wxSize size = m_ctrl->GetClientSize();
72 			m_dspSize = size;
73 			m_dspPos = wxPoint();
74 			if (size.y >= size.x/aspect) {
75 				m_dspSize.y = size.x/aspect;
76 				m_dspPos.y = (size.y - m_dspSize.y)/2;
77 			} else {
78 				m_dspSize.x = size.y*aspect;
79 				m_dspPos.x = (size.x - m_dspSize.x)/2;
80 			}
81 			wxSize bmpSize = m_dspSize;
82 			m_bmpPos = m_dspPos;
83 			if (m_pad.size() == 4 && m_pad[0] + m_pad[1] + m_pad[2] + m_pad[3] > 0) {
84 				int padX = m_pad[0] + m_pad[1];
85 				int padY = m_pad[2] + m_pad[3];
86 				bmpSize.x -= padX*m_dspSize.x/frameSize.x;
87 				bmpSize.y -= padY*m_dspSize.y/frameSize.y;
88 				m_bmpPos.x += m_pad[0]*m_dspSize.x/frameSize.x;
89 				m_bmpPos.y += m_pad[2]*m_dspSize.y/frameSize.y;
90 			}
91 			m_bitmap = wxBitmap(img.Scale(bmpSize.x, bmpSize.y));
92 		}
93 		dc.SetPen(*wxTRANSPARENT_PEN);
94 		dc.SetBrush(*wxBLACK_BRUSH);
95 		dc.DrawRectangle(m_dspPos, m_dspSize);
96 		dc.DrawBitmap(m_bitmap, m_bmpPos);
97 	}
98 
OnResize(wxSizeEvent & event)99 	void OnResize(wxSizeEvent &event) {
100 		m_bitmap = wxBitmap();
101 		m_ctrl->Refresh();
102 	}
103 
104 private:
105 	wxMediaCtrl* m_ctrl;
106 	wxImage m_image;
107 	float m_frameAspectRatio;
108 	VideoFormat m_videoFormat;
109 	AspectRatio m_aspectRatio;
110 	vector<int> m_pad;
111 	vector<int> m_crop;
112 	wxSize m_dspSize;
113 	wxPoint m_dspPos;
114 	wxBitmap m_bitmap;
115 	wxPoint m_bmpPos;
116 	DECLARE_EVENT_TABLE()
117 };
118 
119 BEGIN_EVENT_TABLE(FFEvtHandler, wxEvtHandler)
120   EVT_PAINT(FFEvtHandler::OnPaint)
121   EVT_SIZE(FFEvtHandler::OnResize)
122 END_EVENT_TABLE()
123 
124 /////////////////////////////////////////////////////////////////////////////
125 /////////////////////////// wxFFMediaBackend ////////////////////////////////
126 /////////////////////////////////////////////////////////////////////////////
127 class wxFFMediaBackend: public wxMediaBackendCommonBase {
128 public:
wxFFMediaBackend()129 	wxFFMediaBackend(): m_loaded(false), m_duration(0), m_evtHandler(NULL) {}
~wxFFMediaBackend()130 	virtual ~wxFFMediaBackend() {
131 		if (m_evtHandler != NULL) {
132 			m_ctrl->PopEventHandler(true);
133 			m_evtHandler = NULL;
134 		}
135 	}
136 
CreateControl(wxControl * ctrl,wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,long style,const wxValidator & validator,const wxString & name)137 	virtual bool CreateControl(wxControl* ctrl, wxWindow* parent, wxWindowID id, const wxPoint& pos,
138 			const wxSize& size, long style, const wxValidator& validator, const wxString& name) {
139 		if (!ctrl->wxControl::Create(parent, id, pos, size, style, validator, name))
140 			return false;
141 		m_ctrl = wxStaticCast(ctrl, wxMediaCtrl);
142 		m_evtHandler = new FFEvtHandler(m_ctrl);
143 		m_ctrl->PushEventHandler(m_evtHandler);
144 		return true;
145 	}
146 
Load(const wxString & fileName)147 	virtual bool Load(const wxString& fileName) {
148 		if (fileName.length() && m_decoder.Load(fileName)) {
149 			m_loaded = true;
150 			m_duration = m_decoder.GetDuration();
151 			if (m_duration < 0) {
152 				if (m_decoder.SetPosition(360000, false))
153 					m_duration = m_decoder.GetPosition();
154 				m_decoder.SetPosition(0, false);
155 			}
156 			NotifyMovieLoaded();
157 			m_evtHandler->SetImage(m_decoder.GetNextFrame());
158 			m_evtHandler->SetFrameAspectRatio(m_decoder.GetFrameAspectRatio());
159 			return true;
160 		}
161 		m_loaded = false;
162 		m_evtHandler->SetImage(wxImage());
163 		return false;
164 	}
165 
SetPosition(wxLongLong where)166 	virtual bool SetPosition(wxLongLong where) {
167 		if (!m_loaded)
168 			return false;
169 		double dpos = where.ToDouble()/1000;
170 		if (dpos == 0 && m_decoder.GetPosition() != 0) {
171 			m_decoder.SetPosition(0, false);
172 			for (int i = 0; i < 8; i++) {
173 				m_decoder.GetNextFrame();
174 			}
175 		}
176 		m_decoder.SetPosition(dpos > 1.0 ? dpos - 1.0 : 0.0, dpos != 0);
177 		wxImage image;
178 		for (int i = 0; i < 60; i++) {
179 			image = m_decoder.GetNextFrame();
180 			if (m_decoder.GetPosition() >= dpos)
181 				break;
182 		}
183 		m_evtHandler->SetImage(image);
184 		return true;
185 	}
186 
GetPosition()187 	virtual wxLongLong GetPosition() {
188 		wxLongLong res;
189 		if (m_loaded)
190 			res.Assign(m_decoder.GetPosition()*1000);
191 		return res;
192 	}
193 
GetDuration()194 	virtual wxLongLong GetDuration() {
195 		wxLongLong res;
196 		if (m_loaded)
197 			res.Assign(m_duration*1000);
198 		return res;
199 	}
200 
SetVideoFormat(VideoFormat videoFormat,AspectRatio aspectRatio,const vector<int> & pad,const vector<int> & crop)201 	void SetVideoFormat(VideoFormat videoFormat, AspectRatio aspectRatio, const vector<int>& pad, const vector<int>& crop) {
202 		m_evtHandler->SetVideoFormat(videoFormat, aspectRatio, pad, crop);
203 	}
204 
GetFps()205 	inline double GetFps() {
206 		return m_decoder.GetFps();
207 	}
208 
GetDecoder()209 	inline wxFfmpegMediaDecoder& GetDecoder() {
210 		return m_decoder;
211 	}
212 
GetImage()213 	inline wxImage GetImage() {
214 		return m_evtHandler->GetImage();
215 	}
216 
217 private:
218 	bool m_loaded;
219 	wxFfmpegMediaDecoder m_decoder;
220 	double m_duration;
221 	FFEvtHandler* m_evtHandler;
222 	DECLARE_DYNAMIC_CLASS(wxFFMediaBackend)
223 };
224 
IMPLEMENT_DYNAMIC_CLASS(wxFFMediaBackend,wxMediaBackend)225 IMPLEMENT_DYNAMIC_CLASS(wxFFMediaBackend, wxMediaBackend)
226 
227 /////////////////////////////////////////////////////////////////////////////
228 ////////////////////////////// MediaCtrlFF //////////////////////////////////
229 /////////////////////////////////////////////////////////////////////////////
230 MediaCtrlFF::MediaCtrlFF(wxWindow* parent, wxWindowID id, const wxString& fileName, const wxPoint& pos,
231 		const wxSize& size, long style, const wxValidator& validator, const wxString& name):
232         wxMediaCtrl(parent, id, fileName, pos, size, style, wxT("wxFFMediaBackend"), validator, name) {
233 	// nothing to do
234 	if (m_imp == NULL) {
235 		m_imp = new wxFFMediaBackend();
236 		if (!m_imp->CreateControl(this, parent, id, pos, size, style, validator, name)) {
237 		    delete m_imp;
238 		    m_imp = NULL;
239 		}
240 	}
241 }
242 
~MediaCtrlFF()243 MediaCtrlFF::~MediaCtrlFF() {
244 	// nothing to do
245 }
246 
SetVideoFormat(VideoFormat videoFormat,AspectRatio aspectRatio,const vector<int> & pad,const vector<int> & crop)247 void MediaCtrlFF::SetVideoFormat(VideoFormat videoFormat, AspectRatio aspectRatio,
248 		const vector<int>& pad, const vector<int>& crop) {
249 	((wxFFMediaBackend*) m_imp)->SetVideoFormat(videoFormat, aspectRatio, pad, crop);
250 }
251 
GetFps()252 double MediaCtrlFF::GetFps() {
253 	return ((wxFFMediaBackend*) m_imp)->GetFps();
254 }
255 
GetDecoder()256 wxFfmpegMediaDecoder& MediaCtrlFF::GetDecoder() {
257 	return ((wxFFMediaBackend*) m_imp)->GetDecoder();
258 }
259 
GetImage()260 wxImage MediaCtrlFF::GetImage() {
261 	return ((wxFFMediaBackend*) m_imp)->GetImage();
262 }
263