1 /* GemRB - Infinity Engine Emulator
2  * Copyright (C) 2012 The GemRB Project
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  *
19  */
20 
21 #include "VLCPlayer.h"
22 #include "Video.h"
23 
24 using namespace GemRB;
25 
VLCPlayer(void)26 VLCPlayer::VLCPlayer(void)
27 {
28 	libvlc = libvlc_new(0, NULL);
29 	mediaPlayer = NULL;
30 	planes[0] = NULL;
31 	planes[1] = NULL;
32 	planes[2] = NULL;
33 }
34 
~VLCPlayer(void)35 VLCPlayer::~VLCPlayer(void)
36 {
37 	DestroyPlayer();
38 	libvlc_media_player_release(mediaPlayer);
39 	libvlc_release(libvlc);
40 }
41 
Open(DataStream * stream)42 bool VLCPlayer::Open(DataStream* stream)
43 {
44 	DestroyPlayer();
45 	if (stream) {
46 		// we don't actually need anything from the stream. libVLC will open and use the file internally
47 		libvlc_media_t* media = libvlc_media_new_path(libvlc, stream->originalfile);
48 		mediaPlayer = libvlc_media_player_new_from_media(media);
49 		libvlc_media_release(media); //player retains the media
50 
51 		libvlc_video_set_callbacks(mediaPlayer, lock, NULL, NULL, this);
52 		libvlc_video_set_format_callbacks(mediaPlayer, setup, NULL);
53 
54 		bool success = libvlc_media_player_play(mediaPlayer) == 0;
55 
56 		// FIXME: this is technically a data race!
57 		while (success && movieFormat == Video::BufferFormat::DISPLAY);
58 
59 		return success;
60 	}
61 	return false;
62 }
63 
DecodeFrame(VideoBuffer & buf)64 bool VLCPlayer::DecodeFrame(VideoBuffer& buf)
65 {
66 	int pitches[3];
67 
68 	switch (movieFormat) {
69 		case Video::BufferFormat::RGB555:
70 			pitches[0] = movieSize.w * 2;
71 			break;
72 		case Video::BufferFormat::YV12:
73 			pitches[Y] = movieSize.w;
74 			pitches[U] = movieSize.w / 2;
75 			pitches[V] = movieSize.w / 2;
76 			break;
77 		default: // 32 bit
78 			pitches[0] = movieSize.w * 4;
79 			break;
80 	}
81 
82 	buf.CopyPixels(Region(0, 0, movieSize.w, movieSize.h),
83 				   planes[0], &pitches[0], // Y or RGB
84 				   planes[1], &pitches[1], // U
85 				   planes[2], &pitches[2]);// V
86 	return true;
87 }
88 
DestroyPlayer()89 void VLCPlayer::DestroyPlayer()
90 {
91 	if (mediaPlayer) {
92 		libvlc_media_player_stop(mediaPlayer);
93 		libvlc_media_player_release(mediaPlayer);
94 	}
95 
96 	for (int i = 0; i < 3; ++i) {
97 		delete[] planes[i];
98 	}
99 }
100 
101 // static vlc callbacks
102 
lock(void * data,void ** planes)103 void* VLCPlayer::lock(void *data, void **planes)
104 {
105 	VLCPlayer* player = static_cast<VLCPlayer*>(data);
106 
107 	planes[0] = player->planes[0];
108 	planes[1] = player->planes[1];
109 	planes[2] = player->planes[2];
110 
111 	return NULL; // we are using a single buffer so return NULL
112 }
113 
setup(void ** opaque,char * chroma,unsigned * width,unsigned * height,unsigned * pitches,unsigned * lines)114 unsigned VLCPlayer::setup(void **opaque, char *chroma, unsigned *width, unsigned *height, unsigned *pitches, unsigned *lines)
115 {
116 	VLCPlayer* player = static_cast<VLCPlayer*>(*opaque);
117 	int w = *width;
118 	int h = *height;
119 	player->movieSize.w = w;
120 	player->movieSize.h = h;
121 
122 	if (strcmp(chroma, "RV16") == 0) { // 16bit RGB
123 		player->movieFormat = Video::BufferFormat::RGB555;
124 
125 		pitches[0] = w * 2;
126 		lines[0] = h;
127 
128 		player->planes[0] = new char[pitches[0] * lines[0]];
129 	} else if (strcmp(chroma, "YV12") == 0 || strcmp(chroma, "I420") == 0) {
130 		player->movieFormat = Video::BufferFormat::YV12;
131 		memcpy(chroma, "YV12", 4); // we prefer this plane order
132 
133 		pitches[Y] = w;
134 		pitches[U] = w / 2;
135 		pitches[V] = w / 2;
136 		lines[Y] = h;
137 		lines[U] = h / 2;
138 		lines[V] = h / 2;
139 
140 		player->planes[Y] = new char[pitches[Y] * lines[Y]];
141 		player->planes[U] = new char[pitches[U] * lines[U]];
142 		player->planes[V] = new char[pitches[V] * lines[V]];
143 	} else { // default to 32bit
144 		player->movieFormat = Video::BufferFormat::RGBA8888;
145 		memcpy(chroma, "RV32", 4);
146 
147 		pitches[0] = w * 4;
148 		lines[0] = h;
149 
150 		player->planes[0] = new char[pitches[0] * lines[0]];
151 	}
152 
153 	return 1; // indicates the number of buffers allocated
154 }
155 
156 #include "plugindef.h"
157 
158 GEMRB_PLUGIN(0x218963DD, "VLC Video Player")
159 // TODO: VLC is quite capable of playing various movie formats
160 // it seems silly to hardcode a single value or add formats piecemeal.
161 // it would be a shame to force modders or new content creators to use a specific format
162 PLUGIN_RESOURCE(VLCPlayer, "mov") // at least some mac ports used Quicktime MOV format
163 PLUGIN_RESOURCE(VLCPlayer, "webm") // EE movies
164 END_PLUGIN()
165