/*
Реализация спецификаций CLDC версии 1.1 (JSR-139), MIDP версии 2.1 (JSR-118)
и других спецификаций для функционирования компактных приложений на языке
Java (мидлетов) в среде программного обеспечения Малик Эмулятор.
Copyright © 2016–2017, 2019–2023, 2025 Малик Разработчик
Это свободная программа: вы можете перераспространять ее и/или изменять
ее на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
в каком она была опубликована Фондом свободного программного обеспечения;
либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.
Эта программа распространяется в надежде, что она будет полезной,
но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
общественной лицензии GNU.
Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
вместе с этой программой. Если это не так, см.
<https://www.gnu.org/licenses/>.
*/
package malik.emulator.microedition.media;
import javax.microedition.media.*;
import malik.emulator.util.*;
public abstract class CustomPlayer extends ControlList implements Player
{
private static final class EventThread extends Thread
{
private final RunnableQueue queue;
public EventThread() {
super("Обработчик событий от проигрывателей");
this.queue = new RunnableQueue();
}
public void run() {
for(RunnableQueue monitor = queue; ; )
{
Runnable event = null;
synchronized(monitor)
{
try
{
monitor.wait();
}
catch(InterruptedException e)
{
e.printRealStackTrace();
}
if(!monitor.isEmpty())
{
event = monitor.peekHeadRunnable();
((Queue) monitor).removeHeadElement();
}
}
if(event != null) event.run();
}
}
public void post(CustomPlayer player, String event, Object data) {
RunnableQueue monitor;
synchronized(monitor = queue)
{
monitor.addTailElement(player.new EventData(event, data));
monitor.notify();
}
}
}
private static final EventThread EVENT;
private static final Long MINUS_ONE;
private static final Long ZERO;
static {
EventThread event;
((Thread) (event = new EventThread())).start();
EVENT = event;
MINUS_ONE = new Long(-1L);
ZERO = new Long(0L);
}
private static Long toLongCover(long value) {
return value == 0L ? ZERO : value == -1L ? MINUS_ONE : new Long(value);
}
private final class EventData extends Object implements Runnable
{
private final String event;
private final Object data;
public EventData(String event, Object data) {
this.event = event;
this.data = data;
}
public void run() {
PlayerListener[] listeners;
CustomPlayer player = CustomPlayer.this;
String event = this.event;
Object data = this.data;
for(int i = (listeners = player.listeners) != null ? listeners.length : 0; i-- > 0; )
{
PlayerListener listener;
if((listener = listeners[i]) != null)
{
try
{
listener.playerUpdate(player, event, data);
}
catch(RuntimeException e)
{
e.printRealStackTrace();
}
}
}
if(PlayerListener.CLOSED.equals(event)) player.listeners = null;
}
}
private boolean realizing;
private int state;
PlayerListener[] listeners;
private Long duration;
protected TimeBase master;
protected String contentType;
protected final Object monitor;
protected CustomPlayer() {
this.state = UNREALIZED;
this.duration = ZERO;
this.master = Manager.getSystemTimeBase();
this.monitor = new Object();
}
public final Control[] getControls() {
int error = 0;
Control[] result;
synchronized(monitor)
{
label0:
{
int state;
if((state = this.state) == UNREALIZED)
{
error = 1;
result = null;
break label0;
}
if(state == CLOSED)
{
error = 2;
result = null;
break label0;
}
result = super.getControls();
}
}
switch(error)
{
case 1:
throw new IllegalStateException("Player.getControls: проигрыватель не загружен данными.");
case 2:
throw new IllegalStateException("Player.getControls: проигрыватель закрыт.");
}
return result;
}
public final Control getControl(String controlTypeName) {
int error = 0;
Control result;
synchronized(monitor)
{
label0:
{
int state;
if((state = this.state) == UNREALIZED)
{
error = 1;
result = null;
break label0;
}
if(state == CLOSED)
{
error = 2;
result = null;
break label0;
}
if(controlTypeName == null)
{
error = 3;
result = null;
break label0;
}
result = super.getControl(controlTypeName);
}
}
switch(error)
{
case 1:
throw new IllegalStateException("Player.getControl: проигрыватель не загружен данными.");
case 2:
throw new IllegalStateException("Player.getControl: проигрыватель закрыт.");
case 3:
throw new IllegalArgumentException("Player.getControl: аргумент controlTypeName равен нулевой ссылке.");
}
return result;
}
public final void close() {
synchronized(monitor)
{
if(state != CLOSED)
{
playerClose();
controls = null;
state = CLOSED;
duration = ZERO;
master = null;
contentType = null;
postEvent(PlayerListener.CLOSED, null);
}
}
}
public final void deallocate() {
int error = 0;
synchronized(monitor)
{
label0:
{
int state;
if((state = this.state) == CLOSED)
{
error = 1;
break label0;
}
if(state == UNREALIZED && realizing)
{
playerUnrealize();
break label0;
}
if(state == STARTED)
{
state = PREFETCHED;
postEvent(PlayerListener.STOPPED, toLongCover(playerGetMediaTime()));
}
if(state == PREFETCHED)
{
playerUnprefetch();
this.state = REALIZED;
postEvent(PlayerListener.DEVICE_UNAVAILABLE, playerGetDeviceName());
}
}
}
if(error == 1)
{
throw new IllegalStateException("Player.deallocate: проигрыватель закрыт.");
}
}
public final void realize() throws MediaException {
boolean realized;
int error = 0;
Object monitor;
MediaException exception;
synchronized(monitor = this.monitor)
{
label0:
{
int state;
if((state = this.state) == UNREALIZED && realizing)
{
error = 1;
break label0;
}
if(state == CLOSED)
{
error = 2;
break label0;
}
if(state == REALIZED || state == PREFETCHED || state == STARTED)
{
error = 3;
break label0;
}
realizing = true;
}
}
switch(error)
{
case 1:
throw new MediaException("Player.realize: проигрыватель уже загружается данными.");
case 2:
throw new IllegalStateException("Player.realize: проигрыватель закрыт.");
case 3:
return;
}
realized = false;
exception = null;
try
{
playerRealize();
realized = true;
}
catch(MediaException e)
{
exception = e;
}
catch(Exception e)
{
}
synchronized(monitor)
{
if(state == UNREALIZED && realized) state = REALIZED;
realizing = false;
}
if(exception != null)
{
throw exception;
}
}
public final void prefetch() throws MediaException {
switch(state)
{
case CLOSED:
throw new IllegalStateException("Player.prefetch: проигрыватель закрыт.");
case UNREALIZED:
realize();
if(state != REALIZED)
{
throw new MediaException("Player.prefetch: проигрыватель не может сейчас получить доступ к ресурсам.");
}
/* fall through */
}
synchronized(monitor)
{
if(state == REALIZED)
{
playerPrefetch();
state = PREFETCHED;
postEvent(PlayerListener.DEVICE_AVAILABLE, playerGetDeviceName());
}
}
}
public final void start() throws MediaException {
switch(state)
{
case CLOSED:
throw new IllegalStateException("Player.start: проигрыватель закрыт.");
case UNREALIZED:
realize();
if(state != REALIZED)
{
throw new MediaException("Player.start: проигрыватель не может сейчас начать проигрывание данных.");
}
/* fall through */
case REALIZED:
prefetch();
if(state != PREFETCHED)
{
throw new MediaException("Player.start: проигрыватель не может сейчас начать проигрывание данных.");
}
/* fall through */
}
synchronized(monitor)
{
if(state == PREFETCHED)
{
long mediaTime = playerGetMediaTime();
playerStart();
state = STARTED;
postEvent(PlayerListener.STARTED, toLongCover(mediaTime));
}
}
}
public final void stop() throws MediaException {
int error = 0;
synchronized(monitor)
{
label0:
{
int state;
if((state = this.state) == CLOSED)
{
error = 1;
break label0;
}
if(state == STARTED)
{
playerStop();
this.state = PREFETCHED;
postEvent(PlayerListener.STOPPED, toLongCover(playerGetMediaTime()));
}
}
}
if(error == 1)
{
throw new IllegalStateException("Player.stop: проигрыватель закрыт.");
}
}
public final void addPlayerListener(PlayerListener listener) {
if(state == CLOSED)
{
throw new IllegalStateException("Player.addPlayerListener: проигрыватель закрыт.");
}
if(listener == null) return;
synchronized(monitor)
{
int len;
PlayerListener[] listeners;
if((listeners = this.listeners) == null) listeners = this.listeners = new PlayerListener[3];
if(Array.findf(listeners, 0, listener) >= (len = listeners.length))
{
int index;
if((index = Array.findf(listeners, 0, null)) >= len)
{
Array.copy(listeners, 0, listeners = new PlayerListener[(len << 1) + 1], 0, len);
this.listeners = listeners;
}
listeners[index] = listener;
}
}
}
public final void removePlayerListener(PlayerListener listener) {
if(state == CLOSED)
{
throw new IllegalStateException("Player.removePlayerListener: проигрыватель закрыт.");
}
if(listener == null) return;
synchronized(monitor)
{
int index;
PlayerListener[] listeners;
if((listeners = this.listeners) != null && (index = Array.findf(listeners, 0, listener)) < listeners.length) listeners[index] = null;
}
}
public final void setLoopCount(int loopCount) {
int error = 0;
synchronized(monitor)
{
label0:
{
int state;
if((state = this.state) == STARTED)
{
error = 1;
break label0;
}
if(state == CLOSED)
{
error = 2;
break label0;
}
if(loopCount == 0)
{
error = 3;
break label0;
}
playerSetLoopCount(loopCount);
}
}
switch(error)
{
case 1:
throw new IllegalStateException("Player.setLoopCount: проигрыватель занят.");
case 2:
throw new IllegalStateException("Player.setLoopCount: проигрыватель закрыт.");
case 3:
throw new IllegalArgumentException("Player.setLoopCount: аргумент loopCount не может быть равен нулю.");
}
}
public final void setTimeBase(TimeBase master) throws MediaException {
int error = 0;
synchronized(monitor)
{
label0:
{
int state;
if((state = this.state) == UNREALIZED)
{
error = 1;
break label0;
}
if(state == STARTED)
{
error = 2;
break label0;
}
if(state == CLOSED)
{
error = 3;
break label0;
}
playerSetTimeBase(master);
}
}
switch(error)
{
case 1:
throw new IllegalStateException("Player.setTimeBase: проигрыватель не загружен данными.");
case 2:
throw new IllegalStateException("Player.setTimeBase: проигрыватель занят.");
case 3:
throw new IllegalStateException("Player.setTimeBase: проигрыватель закрыт.");
}
}
public final int getState() {
return state;
}
public final long setMediaTime(long mediaTime) throws MediaException {
int error = 0;
long result;
synchronized(monitor)
{
label0:
{
int state;
if((state = this.state) == UNREALIZED)
{
error = 1;
result = 0L;
break label0;
}
if(state == CLOSED)
{
error = 2;
result = 0L;
break label0;
}
result = playerSetMediaTime(mediaTime);
}
}
switch(error)
{
case 1:
throw new IllegalStateException("Player.setMediaTime: проигрыватель не загружен данными.");
case 2:
throw new IllegalStateException("Player.setMediaTime: проигрыватель закрыт.");
}
return result;
}
public final long getMediaTime() {
int error = 0;
long result;
synchronized(monitor)
{
label0:
{
if(state == CLOSED)
{
error = 1;
result = 0L;
break label0;
}
result = playerGetMediaTime();
}
}
if(error == 1)
{
throw new IllegalStateException("Player.getMediaTime: проигрыватель закрыт.");
}
return result;
}
public final long getDuration() {
if(state == CLOSED)
{
throw new IllegalStateException("Player.getDuration: проигрыватель закрыт.");
}
return duration.longValue();
}
public final TimeBase getTimeBase() {
switch(state)
{
case CLOSED:
throw new IllegalStateException("Player.getTimeBase: проигрыватель закрыт.");
case UNREALIZED:
throw new IllegalStateException("Player.getTimeBase: проигрыватель не загружен данными.");
}
return master;
}
public final String getContentType() {
switch(state)
{
case CLOSED:
throw new IllegalStateException("Player.getContentType: проигрыватель закрыт.");
case UNREALIZED:
throw new IllegalStateException("Player.getContentType: проигрыватель не загружен данными.");
}
return contentType;
}
public final boolean isRealizing() {
return realizing;
}
protected abstract void playerUnrealize();
protected abstract void playerUnprefetch();
protected abstract void playerRealize() throws MediaException, InterruptedException;
protected abstract void playerPrefetch() throws MediaException;
protected abstract void playerStart() throws MediaException;
protected abstract void playerStop() throws MediaException;
protected abstract void playerSetLoopCount(int loopCount);
protected abstract long playerSetMediaTime(long mediaTime) throws MediaException;
protected abstract long playerGetMediaTime();
protected void postEvent(String event, Object data) {
EVENT.post(this, event, data);
}
protected void playerClose() {
}
protected void playerRestart() {
postEvent(PlayerListener.END_OF_MEDIA, duration);
postEvent(PlayerListener.STARTED, ZERO);
}
protected void playerEndOfMedia() {
state = PREFETCHED;
postEvent(PlayerListener.END_OF_MEDIA, duration);
}
protected void playerSetTimeBase(TimeBase master) throws MediaException {
if(master != Manager.getSystemTimeBase())
{
throw new MediaException("Player.setTimeBase: установка другой базы времени не поддерживается.");
}
}
protected String playerGetDeviceName() {
return "Системный проигрыватель";
}
protected final void setDuration(long duration) {
Long newDuration;
this.duration = newDuration = toLongCover(duration);
postEvent(PlayerListener.DURATION_UPDATED, newDuration);
}
}