/* * Mobicents Media Gateway * * The source code contained in this file is in in the public domain. * It can be used in any project or product without prior permission, * license or royalty payments. There is NO WARRANTY OF ANY KIND, * EXPRESS, IMPLIED OR STATUTORY, INCLUDING, WITHOUT LIMITATION, * THE IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, * AND DATA ACCURACY. We do not warrant or make any representations * regarding the use of the software or the results thereof, including * but not limited to the correctness, accuracy, reliability or * usefulness of the software. */ package org.mobicents.media.server.impl.clock; import java.util.HashMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import org.mobicents.media.MediaSource; import org.mobicents.media.server.spi.Timer; /** * Provides repited execution at a reqular time intervals. * * @author Oleg Kulikov */ public class TimerImpl implements Timer { public static final int _DEFAULT_T_PRIORITY = Thread.MAX_PRIORITY; private transient final ScheduledExecutorService timer = Executors.newSingleThreadScheduledExecutor(new MMSClockThreadFactory()); private int heartBeat = 20; private HashMap<String, ScheduledFuture> controls = new HashMap(); private volatile long timestamp; private Clock clock = new Clock(); private ScheduledFuture clockControl; /** * Creates new instance of the timer. */ public TimerImpl() { } /** * Gets value of interval between timer ticks. * * @return the int value in milliseconds. */ public int getHeartBeat() { return heartBeat; } /** * Modify interval between timer tick * * @param heartBeat * the new value of interval in milliseconds. */ public void setHeartBeat(int heartBeat) { this.heartBeat = heartBeat; } /** * (Non Java-doc.) * * @see org.mobicents.media.server.spi.Timer#getTimestamp() */ public long getTimestamp() { return timestamp; } /** * (Non Java-doc.) * * @see org.mobicents.media.server.spi.Timer#sync(org.mobicents.media.MediaSource) */ public void sync(MediaSource mediaSource) throws IllegalArgumentException { if (mediaSource.getPeriod() > 0) { //period have to be multiple to timer's heard beat. //so we are recalculating the actual packetization period. int period = (mediaSource.getPeriod() / heartBeat) * heartBeat; ScheduledFuture control = timer.scheduleAtFixedRate(mediaSource, 0, period, TimeUnit.MILLISECONDS); controls.put(mediaSource.getId(), control); } else throw new IllegalArgumentException(mediaSource + " can not be synchronized from this source"); } /** * (Non Java-doc.) * * @see org.mobicents.media.server.spi.Timer#unsync(org.mobicents.media.MediaSource) */ public void unsync(MediaSource mediaSource) { ScheduledFuture control = controls.remove(mediaSource.getId()); if (control != null) { control.cancel(false); } } /** * (Non Java-doc.) * * @see org.mobicents.media.server.spi.Timer#start() */ public void start() { if (clockControl == null || clockControl.isCancelled()) { clockControl = timer.scheduleAtFixedRate(clock, heartBeat, heartBeat, TimeUnit.MILLISECONDS); } } /** * (Non Java-doc.) * * @see org.mobicents.media.server.spi.Timer#stop() */ public void stop() { if (clockControl != null && !clockControl.isCancelled()) { clockControl.cancel(false); } } private class Clock implements Runnable { public void run() { timestamp += heartBeat; } } } class MMSClockThreadFactory implements ThreadFactory { public static final AtomicLong sequence = new AtomicLong(0); private ThreadGroup factoryThreadGroup = new ThreadGroup("MMSClockThreadGroup[" + sequence.incrementAndGet() + "]"); public Thread newThread(Runnable r) { Thread t = new Thread(this.factoryThreadGroup, r); t.setPriority(TimerImpl._DEFAULT_T_PRIORITY); // ?? //t.start(); return t; } }