I want to build an application with monodroid to have a live video stream from an IPCamera (with MJpeg format) to my tablet. after digging the internet I found that there is a Mjpeg Library project written in Java from here. it has two files MjpegView.java and MjpegInputStream.Java which I put them both here:
MjpegView.java
package de.mjpegsample.MjpegView;
import java.io.IOException;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class MjpegView extends SurfaceView implements SurfaceHolder.Callback {
public final static int POSITION_UPPER_LEFT = 9;
public final static int POSITION_UPPER_RIGHT = 3;
public final static int POSITION_LOWER_LEFT = 12;
public final static int POSITION_LOWER_RIGHT = 6;
public final static int SIZE_STANDARD = 1;
public final static int SIZE_BEST_FIT = 4;
public final static int SIZE_FULLSCREEN = 8;
private MjpegViewThread thread;
private MjpegInputStream mIn = null;
private boolean showFps = false;
private boolean mRun = false;
private boolean surfaceDone = false;
private Paint overlayPaint;
private int overlayTextColor;
private int overlayBackgroundColor;
private int ovlPos;
private int dispWidth;
private int dispHeight;
private int displayMode;
public class MjpegViewThread extends Thread {
private SurfaceHolder mSurfaceHolder;
private int frameCounter = 0;
private long start;
private Bitmap ovl;
public MjpegViewThread(SurfaceHolder surfaceHolder, Context context) { mSurfaceHolder = surfaceHolder; }
private Rect destRect(int bmw, int bmh) {
int tempx;
int tempy;
if (displayMode == MjpegView.SIZE_STANDARD) {
tempx = (dispWidth / 2) - (bmw / 2);
tempy = (dispHeight / 2) - (bmh / 2);
return new Rect(tempx, tempy, bmw + tempx, bmh + tempy);
}
if (displayMode == MjpegView.SIZE_BEST_FIT) {
float bmasp = (float) bmw / (float) bmh;
bmw = dispWidth;
bmh = (int) (dispWidth / bmasp);
if (bmh > dispHeight) {
bmh = dispHeight;
bmw = (int) (dispHeight * bmasp);
}
tempx = (dispWidth / 2) - (bmw / 2);
tempy = (dispHeight / 2) - (bmh / 2);
return new Rect(tempx, tempy, bmw + tempx, bmh + tempy);
}
if (displayMode == MjpegView.SIZE_FULLSCREEN) return new Rect(0, 0, dispWidth, dispHeight);
return null;
}
public void setSurfaceSize(int width, int height) {
synchronized(mSurfaceHolder) {
dispWidth = width;
dispHeight = height;
}
}
private Bitmap makeFpsOverlay(Paint p, String text) {
Rect b = new Rect();
p.getTextBounds(text, 0, text.length(), b);
int bwidth = b.width()+2;
int bheight = b.height()+2;
Bitmap bm = Bitmap.createBitmap(bwidth, bheight, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bm);
p.setColor(overlayBackgroundColor);
c.drawRect(0, 0, bwidth, bheight, p);
p.setColor(overlayTextColor);
c.drawText(text, -b.left+1, (bheight/2)-((p.ascent()+p.descent())/2)+1, p);
return bm;
}
public void run() {
start = System.currentTimeMillis();
PorterDuffXfermode mode = new PorterDuffXfermode(PorterDuff.Mode.DST_OVER);
Bitmap bm;
int width;
int height;
Rect destRect;
Canvas c = null;
Paint p = new Paint();
String fps = "";
while (mRun) {
if(surfaceDone) {
try {
c = mSurfaceHolder.lockCanvas();
synchronized (mSurfaceHolder) {
try {
bm = mIn.readMjpegFrame();
destRect = destRect(bm.getWidth(),bm.getHeight());
c.drawColor(Color.BLACK);
c.drawBitmap(bm, null, destRect, p);
if(showFps) {
p.setXfermode(mode);
if(ovl != null) {
height = ((ovlPos & 1) == 1) ? destRect.top : destRect.bottom-ovl.getHeight();
width = ((ovlPos & 8) == 8) ? destRect.left : destRect.right -ovl.getWidth();
c.drawBitmap(ovl, width, height, null);
}
p.setXfermode(null);
frameCounter++;
if((System.currentTimeMillis() - start) >= 1000) {
fps = String.valueOf(frameCounter)+"fps";
frameCounter = 0;
start = System.currentTimeMillis();
ovl = makeFpsOverlay(overlayPaint, fps);
}
}
} catch (IOException e) {}
}
} finally { if (c != null) mSurfaceHolder.unlockCanvasAndPost(c); }
}
}
}
}
private void init(Context context) {
SurfaceHolder holder = getHolder();
holder.addCallback(this);
thread = new MjpegViewThread(holder, context);
setFocusable(true);
overlayPaint = new Paint();
overlayPaint.setTextAlign(Paint.Align.LEFT);
overlayPaint.setTextSize(12);
overlayPaint.setTypeface(Typeface.DEFAULT);
overlayTextColor = Color.WHITE;
overlayBackgroundColor = Color.BLACK;
ovlPos = MjpegView.POSITION_LOWER_RIGHT;
displayMode = MjpegView.SIZE_STANDARD;
dispWidth = getWidth();
dispHeight = getHeight();
}
public void startPlayback() {
if(mIn != null) {
mRun = true;
thread.start();
}
}
public void stopPlayback() {
mRun = false;
boolean retry = true;
while(retry) {
try {
thread.join();
retry = false;
} catch (InterruptedException e) {}
}
}
public MjpegView(Context context, AttributeSet attrs) { super(context, attrs); init(context); }
public void surfaceChanged(SurfaceHolder holder, int f, int w, int h) { thread.setSurfaceSize(w, h); }
public void surfaceDestroyed(SurfaceHolder holder) {
surfaceDone = false;
stopPlayback();
}
public MjpegView(Context context) { super(context); init(context); }
public void surfaceCreated(SurfaceHolder holder) { surfaceDone = true; }
public void showFps(boolean b) { showFps = b; }
public void setSource(MjpegInputStream source) { mIn = source; startPlayback();}
public void setOverlayPaint(Paint p) { overlayPaint = p; }
public void setOverlayTextColor(int c) { overlayTextColor = c; }
public void setOverlayBackgroundColor(int c) { overlayBackgroundColor = c; }
public void setOverlayPosition(int p) { ovlPos = p; }
public void setDisplayMode(int s) { displayMode = s; }
}
MjpegInputStream.Java
package de.mjpegsample.MjpegView;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.Properties;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
public class MjpegInputStream extends DataInputStream {
private final byte[] SOI_MARKER = { (byte) 0xFF, (byte) 0xD8 };
private final byte[] EOF_MARKER = { (byte) 0xFF, (byte) 0xD9 };
private final String CONTENT_LENGTH = "Content-Length";
private final static int HEADER_MAX_LENGTH = 100;
private final static int FRAME_MAX_LENGTH = 40000 + HEADER_MAX_LENGTH;
private int mContentLength = -1;
public static MjpegInputStream read(String url) {
HttpResponse res;
DefaultHttpClient httpclient = new DefaultHttpClient();
try {
res = httpclient.execute(new HttpGet(URI.create(url)));
return new MjpegInputStream(res.getEntity().getContent());
} catch (ClientProtocolException e) {
} catch (IOException e) {}
return null;
}
public MjpegInputStream(InputStream in) { super(new BufferedInputStream(in, FRAME_MAX_LENGTH)); }
private int getEndOfSeqeunce(DataInputStream in, byte[] sequence) throws IOException {
int seqIndex = 0;
byte c;
for(int i=0; i < FRAME_MAX_LENGTH; i++) {
c = (byte) in.readUnsignedByte();
if(c == sequence[seqIndex]) {
seqIndex++;
if(seqIndex == sequence.length) return i + 1;
} else seqIndex = 0;
}
return -1;
}
private int getStartOfSequence(DataInputStream in, byte[] sequence) throws IOException {
int end = getEndOfSeqeunce(in, sequence);
return (end < 0) ? (-1) : (end - sequence.length);
}
private int parseContentLength(byte[] headerBytes) throws IOException, NumberFormatException {
ByteArrayInputStream headerIn = new ByteArrayInputStream(headerBytes);
Properties props = new Properties();
props.load(headerIn);
return Integer.parseInt(props.getProperty(CONTENT_LENGTH));
}
public Bitmap readMjpegFrame() throws IOException {
mark(FRAME_MAX_LENGTH);
int headerLen = getStartOfSequence(this, SOI_MARKER);
reset();
byte[] header = new byte[headerLen];
readFully(header);
try {
mContentLength = parseContentLength(header);
} catch (NumberFormatException nfe) {
mContentLength = getEndOfSeqeunce(this, EOF_MARKER);
}
reset();
byte[] frameData = new byte[mContentLength];
skipBytes(headerLen);
readFully(frameData);
return BitmapFactory.decodeStream(new ByteArrayInputStream(frameData));
}
}
so I converted that (actually create a c# wrapper) with Binding Library project.
but although I followed the Sample code tutorial of this project as following:
The sample itself:
public class MjpegSample extends Activity {
private MjpegView mv;
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
//sample public cam
String URL = "http://webcam5.hrz.tu-darmstadt.de/axis-cgi/mjpg/video.cgi?resolution=320x240";
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
mv = new MjpegView(this);
setContentView(mv);
mv.setSource(MjpegInputStream.read(URL));
mv.setDisplayMode(MjpegView.SIZE_BEST_FIT);
mv.showFps(true);
}
What I have Done in Monodroid:
namespace AndroidApplication8
{
[Activity(Label = "AndroidApplication8", MainLauncher = true, Icon = "#drawable/icon")]
public class Activity1 : Activity
{
int count = 1;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
String URL = "rtsp://192.168.1.3/Mjpeg/video.cgi";
var mv = new MjpegView(this);
SetContentView(mv);
**mv.SetSource(MjpegInputStream.Read(URL));
mv.SetDisplayMode(MjpegView.SizeBestFit);
mv.StartPlayback();
}
}
}
but it gives me an error in the line indicated with ** when it wants to execute MjpegInputStream.Read()
and it jumps to the class converted from the native Java files without any more information.
You should check your video type.For example if your video encoding is compressed over there(before getting to your android device) you should encode it before put it into your browser.This could let you write a code in java for example to verify the incoming stream from cameras first(don't use build-in browser of android) and then decode it manually.
Good luck!
Related
i've been trying to use Named Pipes in C# for a while but can't get it to work.
I'm working with the following code:
internal class SplitManager
{
public static void initialize()
{
Debug.Log("Initializing LiveSplit pipe");
SplitManager.pipeClientStream = new NamedPipeClientStream("//.//pipe//LiveSplit");
Debug.Log("Successfully initialized LiveSplit pipe");
}
public static void startRun()
{
Debug.Log("[PIPE]: start");
SplitManager.WriteString("start");
}
public static void performSplit()
{
Debug.Log("[PIPE]: split");
SplitManager.WriteString("split");
}
private static void WriteString(string str)
{
SplitManager.pipeClientStream.Connect();
new StreamString(SplitManager.pipeClientStream).WriteString(str);
SplitManager.pipeClientStream.Close();
}
private static NamedPipeClientStream pipeClientStream;
}
public class StreamString
{
public StreamString(Stream ioStream)
{
this.ioStream = ioStream;
this.streamEncoding = new UnicodeEncoding();
}
public string ReadString()
{
int num = this.ioStream.ReadByte() * 256;
num += this.ioStream.ReadByte();
byte[] array = new byte[num];
this.ioStream.Read(array, 0, num);
return this.streamEncoding.GetString(array);
}
public int WriteString(string outString)
{
byte[] bytes = this.streamEncoding.GetBytes(outString);
int num = bytes.Length;
if (num > 65535)
{
num = 65535;
}
this.ioStream.WriteByte((byte)(num / 256));
this.ioStream.WriteByte((byte)(num & 255));
this.ioStream.Write(bytes, 0, num);
this.ioStream.Flush();
return bytes.Length + 2;
}
private Stream ioStream;
private UnicodeEncoding streamEncoding;
}
When i run this code i get Win32Exception with an error message that it cannot find the specified file. I'm 100% sure the path is fine, since i checked it with powershell command [System.IO.Directory]::GetFiles("\\.\\pipe\\"). Any ideas why does this error happen?
It turns out that C# actually provides the "//.//pipe//" prefix to the string, so simply replacing SplitManager.pipeClientStream = new NamedPipeClientStream("//.//pipe//LiveSplit"); to SplitManager.pipeClientStream = new NamedPipeClientStream("LiveSplit"); worked!
Trying to implement a random walk by moving the ball across the screen. Algorithm is looks like working but debug shows that the paint event doesn't invoked.
First of all, try to do this in pattern MVC. For clarity, I will give the code of these components.
View:
public partial class ViewBallBounce : Form, Observer
{
private ModelRandomWalk modelRandomWalk;
private ControllerRandomWalk controllerRandomWalk;
static int i = 0;
public ViewBallBounce(ModelRandomWalk _modelRandomWalk)
{
InitializeComponent();
this.modelRandomWalk = _modelRandomWalk;
controllerRandomWalk = new ControllerRandomWalk(modelRandomWalk, this, ClientSize.Width, ClientSize.Height);
modelRandomWalk.Register(this);
this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true);
this.UpdateStyles();
}
public void UpdateState()
{
this.Refresh();
}
private void ViewBallBounce_Load(object sender, EventArgs e)
{
controllerRandomWalk.Bounce();
}
private void ViewBallBounce_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
g.Clear(BackColor);
i++;
g.FillEllipse(Brushes.Blue, modelRandomWalk.X, modelRandomWalk.Y,
modelRandomWalk.Width, modelRandomWalk.Height);
g.DrawEllipse(Pens.Black, modelRandomWalk.X, modelRandomWalk.Y,
modelRandomWalk.Width, modelRandomWalk.Height);
}
}
Control:
public class ControllerRandomWalk
{
private ModelRandomWalk modelRandomWalk;
private ViewBallBounce viewBallBounce;
public int clientH, clientW;
public ControllerRandomWalk(ModelRandomWalk modelRandomWalk, ViewBallBounce viewBallBounce, int _width, int _height)
{
this.modelRandomWalk = modelRandomWalk;
this.viewBallBounce = viewBallBounce;
this.clientH = _height;
this.clientW = _width;
}
public int Bounce()
{
return modelRandomWalk.bounce(100, clientH, clientW);
}
}
Model:
public class ModelRandomWalk
{
private int x, y;
private int velocityX, velocityY;
private int width, height;
public int X { get { return x; } }
public int Y { get { return y; } }
public int VelocityX { get { return velocityX; } }
public int VelocityY { get { return velocityY; } }
public int Width { get { return width; } }
public int Height { get { return height; } }
public void updatePosition(int key)
{
if (key == 0) this.x += velocityX;
else this.y += velocityY;
}
public void updateVelocity(int key)
{
if (key == 0) this.velocityX = -this.velocityX;
else this.velocityY = -this.velocityY;
}
public ModelRandomWalk(int _x, int _y, int _width, int _height)
{
this.x = _x;
this.y = _y;
this.width = _width;
this.height = _height;
}
public int bounce(int _steps, int _h, int _w)
{
int steps = _steps;
int clientH = _h, clientW = _w;
for(int i = 0; i < steps; i++)
{
Random rnd = new Random();
int v = rnd.Next(0, 4); // 0 = +v_x, 1 = +v_y, 2 = -v_x, 3 = -v_y
switch (v)
{
case 0:
velocityX++;
break;
case 1:
velocityY++;
break;
case 2:
velocityX--;
break;
case 3:
velocityY--;
break;
}
updatePosition(0);
if (X < 0)
{
x = 6;
updateVelocity(0);
}
else if (X + Width > clientW)
{
x = clientW - 6;
updateVelocity(0);
}
updatePosition(1);
if (Y < 0)
{
y = 6;
updateVelocity(1);
}
else if (Y + Height > clientH)
{
y = clientH - 6;
updateVelocity(1);
}
UpdateObservers();
}
return 0;
}
private ArrayList listeners = new ArrayList();
public void Register(Observer o)
{
listeners.Add(o);
o.UpdateState();
}
public void Deregister(Observer o)
{
listeners.Remove(0);
}
public void UpdateObservers()
{
foreach (Observer el in listeners)
{
el.UpdateState();
}
}
}
Sorry for long post. The main problem in these lines, when ViewBallBounce_Paint doesn't invoke.
public void UpdateState()
{
this.Refresh();
}
private void ViewBallBounce_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
g.Clear(BackColor);
i++; //just checking has it invoked
g.FillEllipse(Brushes.Blue, modelRandomWalk.X, modelRandomWalk.Y,
modelRandomWalk.Width, modelRandomWalk.Height);
g.DrawEllipse(Pens.Black, modelRandomWalk.X, modelRandomWalk.Y,
modelRandomWalk.Width, modelRandomWalk.Height);
}
I have tried a lot of ways to set breakpoints and don't understand why after Refresh nothing happens. By the way, breakpoint in ViewBallBounce_Paint shows very strange modelRandomWalk.X or modelRandomWalk.Y as 224 or -78 and they don't change.
What's could be wrong?
UPD: New form is openned and the ball is drawen but it doesn't move. Changed steps in cycle to 10 and find out ball had moved only after ending the cycle in Model Component. Nevertheless, this.Refresh has worked but the paint event has invoked after 10 iterations. By the way, the movement occurred taking into account the changes in speeds and positions in the Model component. Wrote Thread.Sleep(1000) in ViewBallBounce_Paint but there were no changes.
In Unity3d Webgl I download and write big file (about 100 mb and more) to cache (indexeddb).
I downloaded file with UnityWebRequest and write file in downloadHandler.
Sometimes after call filestream.Write() or filestream.Flush() Chrome will crash.
Exception not available, only crash tab of chrome.
Crash happens abount in 50 percent of dowloads.
public class Downloader : MonoBehaviour
{
public IEnumerator DownloadFileCoroutine(string url, string filePath, string clientName, int fileId, Action<int> downloadedCallback)
{
var currentWebRequest = UnityWebRequest.Get(url);
currentWebRequest.downloadHandler = new ToFileDownloadHandler(new byte[64 * 1024], filePath);
var downloadHendler = (currentWebRequest.downloadHandler as ToFileDownloadHandler);
currentWebRequest.Send();
while (!currentWebRequest.isDone && !currentWebRequest.isNetworkError && !currentWebRequest.isHttpError)
{
if (currentWebRequest.isNetworkError)
{
yield break;
}
}
if (currentWebRequest.isNetworkError)
{
downloadHendler.Cancel();
}
else
{
/// suceess
}
}
public class ToFileDownloadHandler : DownloadHandlerScript
{
private int _expected = -1;
private long _received = 0;
private readonly string _filepath;
private readonly FileStream _fileStream = null;
private bool _canceled = false;
public ToFileDownloadHandler(byte[] buffer, string filepath) : base(buffer)
{
CreateDir();
_filepath = filepath;
_fileStream = new FileStream(filepath, FileMode.Create, FileAccess.Write);
}
protected override byte[] GetData() { return null; }
protected override bool ReceiveData(byte[] data, int dataLength)
{
if (data == null || data.Length < 1)
{
return false;
}
_received += dataLength;
if (!_canceled)
{
_fileStream.Write(data, 0, dataLength);
_fileStream.Flush();
}
return true;
}
protected override float GetProgress()
{
if (_expected < 0) return 0;
return (float)_received / _expected;
}
protected override void CompleteContent()
{
_fileStream.Close();
_isComplete = true;
}
protected override void ReceiveContentLength(int contentLength)
{
_expected = contentLength;
}
public void Cancel()
{
if (_canceled) return;
_canceled = true;
if (_fileStream != null)
{
_fileStream.Close();
}
File.Delete(_filepath);
}
private void CreateDir()
{
cachePath = Application.persistentDataPath;
if (!Directory.Exists(cachePath))
{
Directory.CreateDirectory(cachePath);
}
}
}
}
I'm need rename field "Overlay", but not remove.
I'm try make link native jar lib to xamarin dll.
I`m create new Binding Libriary project and include jar file inside.
But when i'm try build solution, system output window get error
'Overlay': member names cannot be the same as their enclosing type.
I'm try configurate MetaData file in the following way.
<metadata>
<remove-node path="/api/package[#name='Com.Cdcom.Naviapps.Progorod']/class[#name='Overlay']/method[#name='Overlay']" />
</metadata>
or
<remove-node path="/api/package[#name='Com.Cdcom.Naviapps.Progorod']/class[#name='Overlay']" />
or i'm try change EnumMethods
<enum-method-mappings>
<mapping jni-class="/api/package[#name='com.cdcom.naviapps.progorod']/class[#name='Overlay']">
<method jni-name="Overlay" parameter="return" clr-enum-type="Android.OS.Overlay" />
</mapping>
</enum-method-mappings>
but i'm get other error
"generator.exe" exited with code -532462766.
You can see the class from first error below
// Metadata.xml XPath class reference: path="/api/package[#name='com.cdcom.naviapps.progorod']/class[#name='Overlay']"
[global::Android.Runtime.Register ("com/cdcom/naviapps/progorod/Overlay", DoNotGenerateAcw=true)]
public partial class Overlay : global::Java.Lang.Object {
//region "Event implementation for Com.Cdcom.Naviapps.Progorod.Overlay.IOnOverlayListener"
public event EventHandler<global::Com.Cdcom.Naviapps.Progorod.Overlay.OverlayEventArgs> Overlay {
add {
global::Java.Interop.EventHelper.AddEventHandler<global::Com.Cdcom.Naviapps.Progorod.Overlay.IOnOverlayListener, global::Com.Cdcom.Naviapps.Progorod.Overlay.IOnOverlayListenerImplementor>(
ref weak_implementor___SetOnOverlayListener,
__CreateIOnOverlayListenerImplementor,
__v => OnOverlayListener = __v,
__h => __h.Handler += value);
}
remove {
global::Java.Interop.EventHelper.RemoveEventHandler<global::Com.Cdcom.Naviapps.Progorod.Overlay.IOnOverlayListener, global::Com.Cdcom.Naviapps.Progorod.Overlay.IOnOverlayListenerImplementor>(
ref weak_implementor___SetOnOverlayListener,
global::Com.Cdcom.Naviapps.Progorod.Overlay.IOnOverlayListenerImplementor.__IsEmpty,
__v => OnOverlayListener = null,
__h => __h.Handler -= value);
}
}
//endregion
}
Original java code
public class Overlay
{
public List<OverlayItem> getItems()
{
return this.mOverlayItems;
}
public OnOverlayListener getOnOverlayListener()
{
return this.mOverlayListener;
}
public void populate()
{
double[] latlon = new double[this.mOverlayItems.size() * 2];
int d = 0;
for (OverlayItem oi : this.mOverlayItems)
{
latlon[(d++)] = oi.getGeoPoint().getLatitude();
latlon[(d++)] = oi.getGeoPoint().getLongitude();
}
Native.populateOverlay(this.mId, latlon);
}
public void setBitmap(Bitmap bitmap, float xOffset, float yOffset, boolean isPlain, int sizeInMeters)
{
int width = 0;
int height = 0;
int[] pixels = null;
if (bitmap != null)
{
width = bitmap.getWidth();
height = bitmap.getHeight();
pixels = new int[width * height];
bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
}
Native.setOverlayBitmap(this.mId, width, height, pixels, xOffset, yOffset, isPlain, sizeInMeters);
}
public void setOnOverlayListener(OnOverlayListener listener)
{
this.mOverlayListener = listener;
}
public static int SPECIAL_OVERLAY_START_ROUTE = -1;
public static int SPECIAL_OVERLAY_FINISH_ROUTE = -2;
public static int SPECIAL_OVERLAY_ROUTE_SPRITE = -3;
public static int SPECIAL_OVERLAY_GEOBLOG_SPRITE = -4;
private static int SPECIAL_OVERLAYS_COUNT = 4;
public static Overlay specialOverlay(int id)
{
if (mSpecialOverlays[(id + SPECIAL_OVERLAYS_COUNT)] == null)
{
mSpecialOverlays[(id + SPECIAL_OVERLAYS_COUNT)] = new Overlay();
mSpecialOverlays[(id + SPECIAL_OVERLAYS_COUNT)].mId = id;
}
return mSpecialOverlays[(id + SPECIAL_OVERLAYS_COUNT)];
}
protected int getId()
{
return this.mId;
}
protected int mId = mNextId++;
private static int mNextId = 1;
private List<OverlayItem> mOverlayItems = new ArrayList();
private OnOverlayListener mOverlayListener;
private static Overlay[] mSpecialOverlays = new Overlay[SPECIAL_OVERLAYS_COUNT];
public static abstract interface OnOverlayListener
{
public abstract void onOverlayEvent(Overlay paramOverlay, OverlayItem paramOverlayItem);
}
}
To debug a firewall delay issue I need an application that will produce a beep on server side when it detects an HTTP GET request.
This code (test.ashx):
<%# WebHandler Language="C#" Class="TestHandler" %>
using System;
using System.Web;
public class TestHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
HttpResponse Response = context.Response;
try
{
Response.Write("Before beep");
Console.Beep();
Response.Write("After beep");
}
catch (Exception ex)
{
Response.Write(ex.Message + "<br />\n" + ex.InnerException.Message);
}
}
public bool IsReusable { get { return false; } }
}
produces sound only when debugging in IIS Express. After moving the web app to IIS, the sound disappears.
The three easy ways of producing a sound are System.Console.Beep(), System.Media.SoundPlayer, and System.Media.SystemSounds.Beep().
Unfortunately, these methods only work in desktop applications, and won't work in service applications. When ASP.Net apps are run under IIS Express (a desktop app), these sound methods work. However, when ASP.Net apps are run under the IIS service, the sound methods don't work.
System.Console.Beep() ultimately calls the kernel32.dll Beep() function. It's restricted to desktop apps only (scroll down to the Requirements section).
Same for System.Media.SoundPlayer and System.Media.SystemSounds.Beep(). They call the kernel32.dll MessageBeep() and the winmm.dll PlaySound() functions, respectively. They, too, are restricted to desktop apps.
One way to get sounds to play in a service is use NAudio. It's easy to install via NuGet.
This chunk of code is the only way I could get the sound to play. It has to be played on a separate worker thread, and the execution of the worker thread needs to be paused to let the .wav file finish playing.
using System;
using System.Diagnostics;
using System.Threading;
using NAudio.Dsp;
using NAudio.Wave;
...
protected void Button1_Click(object sender, EventArgs e)
{
var waveFilename = #"c:\Windows\Media\tada.wav";
/* Trying to play the .wav file on the main thread
doesn't seem to work. */
ThreadPool.QueueUserWorkItem(
(state) =>
{
using (var audioPlayback = new AudioPlayback())
{
audioPlayback.Load(waveFilename);
audioPlayback.Play(); // Asynchronous.
/* Need to sleep for the approximate length of .wav file,
otherwise no sound is produced because of the
asynchronous Play() call. */
Thread.Sleep(2000);
}
});
}
Here's the supporting code taken from code in NAudio's NAudioWPFDemo project:
public class MaxSampleEventArgs : EventArgs
{
[DebuggerStepThrough]
public MaxSampleEventArgs(float minValue, float maxValue)
{
this.MaxSample = maxValue;
this.MinSample = minValue;
}
public float MaxSample { get; private set; }
public float MinSample { get; private set; }
}
public class FftEventArgs : EventArgs
{
[DebuggerStepThrough]
public FftEventArgs(Complex[] result)
{
this.Result = result;
}
public Complex[] Result { get; private set; }
}
public class SampleAggregator : ISampleProvider
{
// volume
public event EventHandler<MaxSampleEventArgs> MaximumCalculated;
private float maxValue;
private float minValue;
public int NotificationCount { get; set; }
int count;
// FFT
public event EventHandler<FftEventArgs> FftCalculated;
public bool PerformFFT { get; set; }
private readonly Complex[] fftBuffer;
private readonly FftEventArgs fftArgs;
private int fftPos;
private readonly int fftLength;
private int m;
private readonly ISampleProvider source;
private readonly int channels;
public SampleAggregator(ISampleProvider source, int fftLength = 1024)
{
channels = source.WaveFormat.Channels;
if (!IsPowerOfTwo(fftLength))
throw new ArgumentException("FFT Length must be a power of two");
this.m = (int) Math.Log(fftLength, 2.0);
this.fftLength = fftLength;
this.fftBuffer = new Complex[fftLength];
this.fftArgs = new FftEventArgs(fftBuffer);
this.source = source;
}
private bool IsPowerOfTwo(int x)
{
return (x & (x - 1)) == 0;
}
public void Reset()
{
count = 0;
maxValue = minValue = 0;
}
private void Add(float value)
{
if (PerformFFT && FftCalculated != null)
{
fftBuffer[fftPos].X = (float) (value * FastFourierTransform.HammingWindow(fftPos, fftLength));
fftBuffer[fftPos].Y = 0;
fftPos++;
if (fftPos >= fftBuffer.Length)
{
fftPos = 0;
// 1024 = 2^10
FastFourierTransform.FFT(true, m, fftBuffer);
FftCalculated(this, fftArgs);
}
}
maxValue = Math.Max(maxValue, value);
minValue = Math.Min(minValue, value);
count++;
if (count >= NotificationCount && NotificationCount > 0)
{
if (MaximumCalculated != null)
MaximumCalculated(this, new MaxSampleEventArgs(minValue, maxValue));
Reset();
}
}
public WaveFormat WaveFormat { get { return source.WaveFormat; } }
public int Read(float[] buffer, int offset, int count)
{
var samplesRead = source.Read(buffer, offset, count);
for (int n = 0; n < samplesRead; n += channels)
Add(buffer[n + offset]);
return samplesRead;
}
}
public class AudioPlayback : IDisposable
{
private IWavePlayer _playbackDevice;
private WaveStream _fileStream;
public void Load(string fileName)
{
Stop();
CloseFile();
EnsureDeviceCreated();
OpenFile(fileName);
}
private void CloseFile()
{
if (_fileStream != null)
{
_fileStream.Dispose();
_fileStream = null;
}
}
private void OpenFile(string fileName)
{
try
{
var inputStream = new AudioFileReader(fileName);
_fileStream = inputStream;
var aggregator = new SampleAggregator(inputStream);
aggregator.NotificationCount = inputStream.WaveFormat.SampleRate / 100;
aggregator.PerformFFT = true;
_playbackDevice.Init(aggregator);
}
catch
{
CloseFile();
throw;
}
}
private void EnsureDeviceCreated()
{
if (_playbackDevice == null)
CreateDevice();
}
private void CreateDevice()
{
_playbackDevice = new WaveOut { DesiredLatency = 200 };
}
public void Play()
{
if (_playbackDevice != null && _fileStream != null && _playbackDevice.PlaybackState != PlaybackState.Playing)
_playbackDevice.Play();
}
public void Pause()
{
if (_playbackDevice != null)
_playbackDevice.Pause();
}
public void Stop()
{
if (_playbackDevice != null)
_playbackDevice.Stop();
if (_fileStream != null)
_fileStream.Position = 0;
}
public void Dispose()
{
Stop();
CloseFile();
if (_playbackDevice != null)
_playbackDevice.Dispose();
}
}
Try this System.Media.SystemSounds.Beep.Play();