View Javadoc

1   package org.rpi.mplayer;
2   
3   import java.io.IOException;
4   import java.lang.Thread.State;
5   import java.util.ArrayList;
6   import java.util.List;
7   import java.util.Observable;
8   import java.util.Observer;
9   
10  import org.apache.log4j.Logger;
11  import org.rpi.config.Config;
12  import org.rpi.player.IPlayer;
13  import org.rpi.player.events.EventBase;
14  import org.rpi.player.events.EventFinishedCurrentTrack;
15  import org.rpi.player.events.EventStatusChanged;
16  import org.rpi.player.events.EventUpdateTrackMetaText;
17  import org.rpi.playlist.CustomTrack;
18  
19  public class MPlayer extends Observable   implements IPlayer, Observer  {
20  
21  	private Logger log = Logger.getLogger(MPlayer.class);
22  	private Process process = null;
23  	private OutputReader reader = null;
24  	private InputWriter writer = null;
25  	private PositionThread position = null;
26  
27  	private boolean bPaused = false;
28  	private boolean bPlaying = false;
29  
30  	// private boolean bPreLoad = false;
31  	private boolean bLoading = false;
32  
33  	private TrackInfo trackInfo = null;
34  	private long volume = 100;
35  
36  	private String uniqueId = "";
37  
38  	private CustomTrack current_track = null;
39  	private boolean bMute;//Used to mute when playing a track
40  	
41  	private boolean mute = false; //Used to keep track of mute status
42  
43  	/***
44  	 * Plays the Custom Track
45  	 * 
46  	 * @param track
47  	 * @return
48  	 */
49  	public boolean playTrack(CustomTrack track, long volume, boolean mute) {
50  		uniqueId = track.getUniqueId();
51  		this.volume = volume;
52  		this.bMute = mute;
53  		current_track = track;
54  		log.info("Starting to playTrack Id: " + uniqueId + " " + track.getFullDetails());
55  		String url = track.getUri();
56  		try {
57  			initProcess(url);
58  		} catch (Exception e) {
59  			log.error("Error playTrack: ", e);
60  		}
61  		return true;
62  	}
63  
64  	/***
65  	 * PreLoad the Track ready for when the current Track ends
66  	 */
67  	public void preLoadTrack(CustomTrack track) {
68  	}
69  
70  	/***
71  	 * Used to start a pre loaded track
72  	 */
73  	public void startTrack() {
74  		startPlaying();
75  	}
76  
77  	/***
78  	 * If the Player is Playing we can change tracks.
79  	 * 
80  	 * @param t
81  	 */
82  	public void openFile(CustomTrack t) {
83  	}
84  
85  	public synchronized void startPlaying() {
86  		log.debug("Starting to Play: " + uniqueId);
87  		setVolume(volume);
88  		if (bMute) {
89  			setMute(bMute);
90  		}
91  		log.debug("Started to Play: " + uniqueId);
92  		setStatus("Playing");
93  		setPlaying(true);
94  		bLoading = false;
95  		bPaused = false;
96  		log.debug("PositionThreadState: " + position.getState());
97  		if (position.getState() == State.NEW) {
98  			log.debug("Position Thread is in State NEW Start Thread");
99  			position.start();
100 		}
101 	}
102 
103 	public synchronized void loaded() {
104 	}
105 
106 	public synchronized void playingTrack() {
107 		startPlaying();
108 	}
109 
110 
111 	/***
112 	 * Build the string to start the process
113 	 * 
114 	 * @param url
115 	 * @throws IOException
116 	 */
117 	private void initProcess(String url) throws IOException {
118 		try {
119 			List<String> params = new ArrayList<String>();
120 			params.add(Config.mplayer_path);
121 			params.add("-slave");
122 			params.add("-quiet");
123 			if (Config.mplayer_cache > 0) {
124 				params.add("-cache");
125 				params.add("" + Config.mplayer_cache);
126 			}
127 			if (Config.mplayer_cache_min > 0) {
128 				params.add("-cache-min");
129 				params.add("" + Config.mplayer_cache_min);
130 			}
131 
132 			trackInfo = new TrackInfo();
133 			trackInfo.addObserver(this);
134 			if (isPlayList(url)) {
135 				params.add("-playlist");
136 			}
137 			params.add(url);
138 			ProcessBuilder builder = new ProcessBuilder(params);
139 			builder.redirectErrorStream(true);
140 			process = builder.start();
141 			log.debug("Create new InputWriter");
142 			writer = new InputWriter(process);
143 			log.debug("Create new OutputReader");
144 			reader = new OutputReader(this);
145 			log.debug("Create new Position Thread");
146 			position = new PositionThread(this);
147 			log.debug("Create new Position Thread");
148 			position.setNewTrack(true);
149 			reader.start();
150 		} catch (Exception e) {
151 			log.error("Error initProcess: ", e);
152 		}
153 	}
154 
155 	/***
156 	 * Is this one of the playlists we have configured
157 	 * 
158 	 * @param url
159 	 * @return
160 	 */
161 	private boolean isPlayList(String url) {
162 		for (String s : Config.playlists) {
163 			if (url.toLowerCase().contains(s.toLowerCase())) {
164 				return true;
165 			}
166 		}
167 		return false;
168 	}
169 
170 	@Override
171 	public void pause(boolean bPause) {
172 		bPaused = true;
173 		log.debug("Sending: pause");
174 		sendCommand("pause");
175 		EventStatusChanged ev = new EventStatusChanged();
176 		ev.setStatus("Paused");
177 		fireEvent(ev);
178 	}
179 
180 	@Override
181 	public synchronized void stop() {
182 		log.debug("Sending: quit");
183 		sendCommand("quit");
184 		EventStatusChanged ev = new EventStatusChanged();
185 		ev.setStatus("Stopped");
186 		fireEvent(ev);
187 	}
188 
189 	@Override
190 	public synchronized void destroy() {
191 		log.debug("Attempting to Stop MPlayer");
192 		sendCommand("quit");
193 		reader = null;
194 		writer = null;
195 		if (position != null) {
196 			position.interrupt();
197 			position = null;
198 		}
199 	}
200 
201 	public synchronized InputWriter getCommandWriter() {
202 		return writer;
203 	}
204 
205 	/**
206 	 * @return the bPaused
207 	 */
208 	public boolean isbPaused() {
209 		return bPaused;
210 	}
211 
212 	/**
213 	 * @return the bPlaying
214 	 */
215 	public boolean isPlaying() {
216 		if (process != null) {
217 			if (position != null) {
218 				return true;
219 			}
220 		}
221 		return false;
222 	}
223 
224 	/**
225 	 * @param bPlaying
226 	 *            the bPlaying to set
227 	 */
228 	public void setPlaying(boolean bPlaying) {
229 		log.debug("setPlaying: " + bPlaying);
230 		this.bPlaying = bPlaying;
231 	}
232 
233 	/***
234 	 * Track has stopped Playing, get Next Track..
235 	 */
236 	public synchronized void stoppedPlaying() {
237 		writer.setStopSendingCommands(true);
238 		log.debug("Stopped Playing get next track: ");
239 		setPlaying(false);
240 		setStatus("Stopped");
241 		position.interrupt();
242 		position = null;
243 		reader = null;
244 		EventFinishedCurrentTrack ev = new EventFinishedCurrentTrack();
245 		fireEvent(ev);
246 	}
247 
248 	/***
249 	 * Update OpenHome with the new Status
250 	 * 
251 	 * @param status
252 	 */
253 	public synchronized void setStatus(String status) {
254 		EventStatusChanged ev = new EventStatusChanged();
255 		ev.setStatus(status);
256 		ev.setTrack(current_track);
257 		fireEvent(ev);
258 	}
259 
260 	public synchronized Process getProcess() {
261 		return this.process;
262 	}
263 
264 	/**
265 	 * @return the trackInfo
266 	 */
267 	public synchronized TrackInfo getTrackInfo() {
268 		return trackInfo;
269 	}
270 
271 	@Override
272 	public void setMute(boolean mute) {
273 		this.mute = mute;
274 		if (mute) {
275 			sendCommand("pausing_keep mute");
276 			// Bug Fix for MPlayer on Raspberry
277 			// sendCommand("pausing_keep volume 0 1");
278 		} else {
279 			sendCommand("pausing_keep mute");
280 			// Bug fix for MPlayer on Raspberry
281 			sendCommand("pausing_keep volume " + (volume - 1) + " 1");
282 			setVolume(volume);
283 		}
284 	}
285 
286 	@Override
287 	public void setVolume(long volume) {
288 		this.volume = volume;
289 		if(!mute)
290 			sendCommand("pausing_keep volume " + volume + " 1");
291 	}
292 	
293 	@Override
294 	public void seekAbsolute(long seconds) {
295 		sendCommand("seek " + seconds + " 2");
296 	}
297 
298 	/***
299 	 * Used by the ICY info to update the track being played on the Radio
300 	 * 
301 	 * @param artist
302 	 * @param title
303 	 */
304 	public synchronized void updateInfo(String artist, String title) {
305 		EventUpdateTrackMetaText ev = new EventUpdateTrackMetaText();
306 		ev.setArtist(artist);
307 		ev.setTitle(title);
308 		fireEvent(ev);
309 	}
310 
311 	/***
312 	 * 
313 	 */
314 	public void endPositionThread() {
315 		if (position != null) {
316 			try {
317 				position.interrupt();
318 			} catch (Exception e) {
319 				log.error("Error Stopping Position Thread", e);
320 			}
321 		}
322 	}
323 
324 	/***
325 	 * 
326 	 */
327 	@Override
328 	public synchronized void resume() {
329 		bPaused = false;
330 		sendCommand("pause");
331 		EventStatusChanged ev = new EventStatusChanged();
332 		ev.setStatus("Playing");
333 		fireEvent(ev);
334 	}
335 
336 	/***
337 	 * Send Command to MPlayer
338 	 * 
339 	 * @param command
340 	 */
341 	public synchronized void sendCommand(String command) {
342 		if (writer != null) {
343 			log.debug("Sending: " + command + " TrackId: " + uniqueId);
344 			writer.sendCommand(command);
345 			log.debug("Sent: " + command + " TrackId: " + uniqueId);
346 		} else {
347 			log.info("Could Not Send Command, Writer was null: " + command);
348 		}
349 	}
350 
351 	public synchronized void fireEvent(EventBase ev) {
352 		setChanged();
353 		notifyObservers(ev);
354 	}
355 
356 	public boolean isLoading() {
357 		return bLoading;
358 	}
359 
360 	@Override
361 	public String getUniqueId() {
362 		return uniqueId;
363 	}
364 
365 	@Override
366 	public String toString() {
367 		StringBuilder sb = new StringBuilder();
368 		sb.append("UniqueId: " + uniqueId);
369 		sb.append("Writer: " + writer.toString());
370 		sb.append("Reader: " + reader.toString());
371 		return sb.toString();
372 	}
373 
374 	@Override
375 	public void update(Observable o, Object evt) {
376 		EventBase e = (EventBase)evt;
377 		fireEvent(e);
378 	}
379 
380 
381 }