1 #include "components/VideoVlcComponent.h"
2
3 #include "renderers/Renderer.h"
4 #include "resources/TextureResource.h"
5 #include "utils/StringUtil.h"
6 #include "PowerSaver.h"
7 #include "Settings.h"
8 #include <vlc/vlc.h>
9 #include <SDL_mutex.h>
10
11 #ifdef WIN32
12 #include <codecvt>
13 #endif
14
15 libvlc_instance_t* VideoVlcComponent::mVLC = NULL;
16
17 // VLC prepares to render a video frame.
lock(void * data,void ** p_pixels)18 static void *lock(void *data, void **p_pixels) {
19 struct VideoContext *c = (struct VideoContext *)data;
20 SDL_LockMutex(c->mutex);
21 SDL_LockSurface(c->surface);
22 *p_pixels = c->surface->pixels;
23 return NULL; // Picture identifier, not needed here.
24 }
25
26 // VLC just rendered a video frame.
unlock(void * data,void *,void * const *)27 static void unlock(void *data, void* /*id*/, void *const* /*p_pixels*/) {
28 struct VideoContext *c = (struct VideoContext *)data;
29 SDL_UnlockSurface(c->surface);
30 SDL_UnlockMutex(c->mutex);
31 }
32
33 // VLC wants to display a video frame.
display(void *,void *)34 static void display(void* /*data*/, void* /*id*/) {
35 //Data to be displayed
36 }
37
VideoVlcComponent(Window * window,std::string subtitles)38 VideoVlcComponent::VideoVlcComponent(Window* window, std::string subtitles) :
39 VideoComponent(window),
40 mMediaPlayer(nullptr)
41 {
42 memset(&mContext, 0, sizeof(mContext));
43
44 // Get an empty texture for rendering the video
45 mTexture = TextureResource::get("");
46
47 // Make sure VLC has been initialised
48 setupVLC(subtitles);
49 }
50
~VideoVlcComponent()51 VideoVlcComponent::~VideoVlcComponent()
52 {
53 stopVideo();
54 }
55
setResize(float width,float height)56 void VideoVlcComponent::setResize(float width, float height)
57 {
58 mTargetSize = Vector2f(width, height);
59 mTargetIsMax = false;
60 mStaticImage.setResize(width, height);
61 resize();
62 }
63
setMaxSize(float width,float height)64 void VideoVlcComponent::setMaxSize(float width, float height)
65 {
66 mTargetSize = Vector2f(width, height);
67 mTargetIsMax = true;
68 mStaticImage.setMaxSize(width, height);
69 resize();
70 }
71
resize()72 void VideoVlcComponent::resize()
73 {
74 if(!mTexture)
75 return;
76
77 const Vector2f textureSize((float)mVideoWidth, (float)mVideoHeight);
78
79 if(textureSize == Vector2f::Zero())
80 return;
81
82 // SVG rasterization is determined by height (see SVGResource.cpp), and rasterization is done in terms of pixels
83 // if rounding is off enough in the rasterization step (for images with extreme aspect ratios), it can cause cutoff when the aspect ratio breaks
84 // so, we always make sure the resultant height is an integer to make sure cutoff doesn't happen, and scale width from that
85 // (you'll see this scattered throughout the function)
86 // this is probably not the best way, so if you're familiar with this problem and have a better solution, please make a pull request!
87
88 if(mTargetIsMax)
89 {
90
91 mSize = textureSize;
92
93 Vector2f resizeScale((mTargetSize.x() / mSize.x()), (mTargetSize.y() / mSize.y()));
94
95 if(resizeScale.x() < resizeScale.y())
96 {
97 mSize[0] *= resizeScale.x();
98 mSize[1] *= resizeScale.x();
99 }else{
100 mSize[0] *= resizeScale.y();
101 mSize[1] *= resizeScale.y();
102 }
103
104 // for SVG rasterization, always calculate width from rounded height (see comment above)
105 mSize[1] = Math::round(mSize[1]);
106 mSize[0] = (mSize[1] / textureSize.y()) * textureSize.x();
107
108 }else{
109 // if both components are set, we just stretch
110 // if no components are set, we don't resize at all
111 mSize = mTargetSize == Vector2f::Zero() ? textureSize : mTargetSize;
112
113 // if only one component is set, we resize in a way that maintains aspect ratio
114 // for SVG rasterization, we always calculate width from rounded height (see comment above)
115 if(!mTargetSize.x() && mTargetSize.y())
116 {
117 mSize[1] = Math::round(mTargetSize.y());
118 mSize[0] = (mSize.y() / textureSize.y()) * textureSize.x();
119 }else if(mTargetSize.x() && !mTargetSize.y())
120 {
121 mSize[1] = Math::round((mTargetSize.x() / textureSize.x()) * textureSize.y());
122 mSize[0] = (mSize.y() / textureSize.y()) * textureSize.x();
123 }
124 }
125
126 // mSize.y() should already be rounded
127 mTexture->rasterizeAt((size_t)Math::round(mSize.x()), (size_t)Math::round(mSize.y()));
128
129 onSizeChanged();
130 }
131
render(const Transform4x4f & parentTrans)132 void VideoVlcComponent::render(const Transform4x4f& parentTrans)
133 {
134 if (!isVisible())
135 return;
136
137 VideoComponent::render(parentTrans);
138 Transform4x4f trans = parentTrans * getTransform();
139 GuiComponent::renderChildren(trans);
140 Renderer::setMatrix(trans);
141
142 if (mIsPlaying && mContext.valid)
143 {
144 const unsigned int fadeIn = (unsigned int)(Math::clamp(0.0f, mFadeIn, 1.0f) * 255.0f);
145 const unsigned int color = Renderer::convertColor((fadeIn << 24) | (fadeIn << 16) | (fadeIn << 8) | 255);
146 Renderer::Vertex vertices[4];
147
148 vertices[0] = { { 0.0f , 0.0f }, { 0.0f, 0.0f }, color };
149 vertices[1] = { { 0.0f , mSize.y() }, { 0.0f, 1.0f }, color };
150 vertices[2] = { { mSize.x(), 0.0f }, { 1.0f, 0.0f }, color };
151 vertices[3] = { { mSize.x(), mSize.y() }, { 1.0f, 1.0f }, color };
152
153 // round vertices
154 for(int i = 0; i < 4; ++i)
155 vertices[i].pos.round();
156
157 // Build a texture for the video frame
158 mTexture->initFromPixels((unsigned char*)mContext.surface->pixels, mContext.surface->w, mContext.surface->h);
159 mTexture->bind();
160
161 // Render it
162 Renderer::drawTriangleStrips(&vertices[0], 4);
163 }
164 else
165 {
166 VideoComponent::renderSnapshot(parentTrans);
167 }
168 }
169
setupContext()170 void VideoVlcComponent::setupContext()
171 {
172 if (!mContext.valid)
173 {
174 // Create an RGBA surface to render the video into
175 mContext.surface = SDL_CreateRGBSurface(SDL_SWSURFACE, (int)mVideoWidth, (int)mVideoHeight, 32, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff);
176 mContext.mutex = SDL_CreateMutex();
177 mContext.valid = true;
178 resize();
179 }
180 }
181
freeContext()182 void VideoVlcComponent::freeContext()
183 {
184 if (mContext.valid)
185 {
186 SDL_FreeSurface(mContext.surface);
187 SDL_DestroyMutex(mContext.mutex);
188 mContext.valid = false;
189 }
190 }
191
setupVLC(std::string subtitles)192 void VideoVlcComponent::setupVLC(std::string subtitles)
193 {
194 // If VLC hasn't been initialised yet then do it now
195 if (!mVLC)
196 {
197 const char** args;
198 const char* newargs[] = { "--quiet", "--sub-file", subtitles.c_str() };
199 const char* singleargs[] = { "--quiet" };
200 int argslen = 0;
201
202 if (!subtitles.empty())
203 {
204 argslen = sizeof(newargs) / sizeof(newargs[0]);
205 args = newargs;
206 }
207 else
208 {
209 argslen = sizeof(singleargs) / sizeof(singleargs[0]);
210 args = singleargs;
211 }
212 mVLC = libvlc_new(argslen, args);
213 }
214 }
215
handleLooping()216 void VideoVlcComponent::handleLooping()
217 {
218 if (mIsPlaying && mMediaPlayer)
219 {
220 libvlc_state_t state = libvlc_media_player_get_state(mMediaPlayer);
221 if (state == libvlc_Ended)
222 {
223 if (!Settings::getInstance()->getBool("VideoAudio") ||
224 (Settings::getInstance()->getBool("ScreenSaverVideoMute") && mScreensaverMode))
225 {
226 libvlc_audio_set_mute(mMediaPlayer, 1);
227 }
228 //libvlc_media_player_set_position(mMediaPlayer, 0.0f);
229 libvlc_media_player_set_media(mMediaPlayer, mMedia);
230 libvlc_media_player_play(mMediaPlayer);
231 }
232 }
233 }
234
startVideo()235 void VideoVlcComponent::startVideo()
236 {
237 if (!mIsPlaying) {
238 mVideoWidth = 0;
239 mVideoHeight = 0;
240
241 #ifdef WIN32
242 std::string path(Utils::String::replace(mVideoPath, "/", "\\"));
243 #else
244 std::string path(mVideoPath);
245 #endif
246 // Make sure we have a video path
247 if (mVLC && (path.size() > 0))
248 {
249 // Set the video that we are going to be playing so we don't attempt to restart it
250 mPlayingVideoPath = mVideoPath;
251
252 // Open the media
253 mMedia = libvlc_media_new_path(mVLC, path.c_str());
254 if (mMedia)
255 {
256 unsigned track_count;
257 // Get the media metadata so we can find the aspect ratio
258 libvlc_media_parse(mMedia);
259 libvlc_media_track_t** tracks;
260 track_count = libvlc_media_tracks_get(mMedia, &tracks);
261 for (unsigned track = 0; track < track_count; ++track)
262 {
263 if (tracks[track]->i_type == libvlc_track_video)
264 {
265 mVideoWidth = tracks[track]->video->i_width;
266 mVideoHeight = tracks[track]->video->i_height;
267 break;
268 }
269 }
270 libvlc_media_tracks_release(tracks, track_count);
271
272 // Make sure we found a valid video track
273 if ((mVideoWidth > 0) && (mVideoHeight > 0))
274 {
275 #ifndef _RPI_
276 if (mScreensaverMode)
277 {
278 if(!Settings::getInstance()->getBool("CaptionsCompatibility")) {
279
280 Vector2f resizeScale((Renderer::getScreenWidth() / (float)mVideoWidth), (Renderer::getScreenHeight() / (float)mVideoHeight));
281
282 if(resizeScale.x() < resizeScale.y())
283 {
284 mVideoWidth = (unsigned int) (mVideoWidth * resizeScale.x());
285 mVideoHeight = (unsigned int) (mVideoHeight * resizeScale.x());
286 }else{
287 mVideoWidth = (unsigned int) (mVideoWidth * resizeScale.y());
288 mVideoHeight = (unsigned int) (mVideoHeight * resizeScale.y());
289 }
290 }
291 }
292 #endif
293 PowerSaver::pause();
294 setupContext();
295
296 // Setup the media player
297 mMediaPlayer = libvlc_media_player_new_from_media(mMedia);
298
299 if (!Settings::getInstance()->getBool("VideoAudio") ||
300 (Settings::getInstance()->getBool("ScreenSaverVideoMute") && mScreensaverMode))
301 {
302 libvlc_audio_set_mute(mMediaPlayer, 1);
303 }
304
305 libvlc_media_player_play(mMediaPlayer);
306 libvlc_video_set_callbacks(mMediaPlayer, lock, unlock, display, (void*)&mContext);
307 libvlc_video_set_format(mMediaPlayer, "RGBA", (int)mVideoWidth, (int)mVideoHeight, (int)mVideoWidth * 4);
308
309 // Update the playing state
310 mIsPlaying = true;
311 mFadeIn = 0.0f;
312 }
313 }
314 }
315 }
316 }
317
stopVideo()318 void VideoVlcComponent::stopVideo()
319 {
320 mIsPlaying = false;
321 mStartDelayed = false;
322 // Release the media player so it stops calling back to us
323 if (mMediaPlayer)
324 {
325 libvlc_media_player_stop(mMediaPlayer);
326 libvlc_media_player_release(mMediaPlayer);
327 libvlc_media_release(mMedia);
328 mMediaPlayer = NULL;
329 freeContext();
330 PowerSaver::resume();
331 }
332 }
333