我正在开发一个将JPEG图像流(MJPEG流)发送到远程PC的应用程序。我在更改相机参数中的预览帧速率时遇到问题。似乎无论我设定的速率如何,相机只给我一帧15 fps的帧。起初我认为这是由于图像压缩然后传输数据。但我创建了一个调试实例,我存储了第一个压缩的JPEG图像,并在每次调用onPreviewFrame函数时发送该图像。这导致可靠的15fps,但我将其设置为30fps。任何人都有任何想法为什么fps如此不一致?当我将其设置为压缩每个预览帧并通过wifi发送时,我得到5-40 FPS的速率,它会跳到那里。
以下是我的预览代码(全屏设置相机)
public class Preview extends SurfaceView implements PreviewCallback, SurfaceHolder.Callback {
SurfaceHolder mHolder;
Camera mCamera;
DatagramSocket udpSocket = null;
final int PORT = 1235;
final String IPAddress = "192.168.1.101";
int packetSize = 1024; // Specify max size of each UDP packet
int WIDTH = 640;
int HEIGHT = 480;
int FPS = 30;
int quality = 70;
/**
* Initialize the Camera Preview Holder and create a udpSocket.
* @param context
*/
Preview(Context context) {
super(context);
mHolder = getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
try {
udpSocket = new DatagramSocket();
} catch (SocketException e) {
Log.e(tag, "Error creating UDP Socket!");
}
}
public void surfaceCreated(SurfaceHolder holder) {
mCamera = Camera.open();
try {
mCamera.setPreviewDisplay(holder);
} catch (IOException exception) {
mCamera.release();
mCamera = null;
Log.e(tag, "Error settign Camera Preview Holder!");
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
udpSocket.close();
mCamera.release();
udpSocket = null;
mCamera = null;
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPreviewSize(WIDTH, HEIGHT);
parameters.setPreviewFrameRate(FPS);
parameters.setRotation(90);
mCamera.setParameters(parameters);
mCamera.setPreviewCallback(this);
mCamera.startPreview();
}
/** Compresses the preview framer (data) into a JPEG image and send to a host at
* the specified IPAddress and Port number for playback. Only sends frame if sendFrame
* is true.
* (non-Javadoc)
* @see android.hardware.Camera.PreviewCallback#onPreviewFrame(byte[], android.hardware.Camera)
* @see android.hardware.Camera
*/
public void onPreviewFrame(byte[] data, Camera camera) {
final YuvImage imgPreview = new YuvImage(data, ImageFormat.NV21, WIDTH, HEIGHT, null);
byte[] buffer;
ByteArrayOutputStream jpegOutStream = new ByteArrayOutputStream();
int offset = 0;
int TotalLength, lengthLeft;
DatagramPacket dgpout;
// Compress image into JPEG
imgPreview.compressToJpeg(new Rect(0, 0, imgPreview.getWidth(), imgPreview.getHeight()), quality, jpegOutStream);
buffer = jpegOutStream.toByteArray();
TotalLength = lengthLeft = buffer.length;
// Send frame out. Split into packets of desired length.
try {
while(lengthLeft>packetSize){
dgpout= new DatagramPacket(buffer, offset, packetSize);
offset+=packetSize;
lengthLeft-=packetSize;
dgpout.setAddress(InetAddress.getByName(IPAddress));
dgpout.setPort(PORT);
udpSocket.send(dgpout);
}
if(lengthLeft>0){
dgpout= new DatagramPacket(buffer,offset,lengthLeft);
offset+=lengthLeft;
lengthLeft-=lengthLeft;
dgpout.setAddress(InetAddress.getByName(IPAddress));
dgpout.setPort(PORT);
udpSocket.send(dgpout);
}
Log.i(tag, "Sent Successfully: frame size="+TotalLength +"Buffer Size: "+udpSocket.getSendBufferSize());
} catch (UnknownHostException e1) {
Log.e(tag , "UnknownHostException. Sending failed.");
} catch (IOException e) {
Log.e(tag , "IOException. Sending failed.");
}
}
}
这是我的调试示例,仅用于反复发送相同的JPEG
if(sendFrame){
sendFrame = false;
final YuvImage imgPreview = new YuvImage(data, ImageFormat.NV21, WIDTH, HEIGHT, null);
fixImage = data;
byte[] buffer;
ByteArrayOutputStream jpegOutStream = new ByteArrayOutputStream();
int offset = 0;
int TotalLength, lengthLeft;
DatagramPacket dgpout;
// Compress image into JPEG
imgPreview.compressToJpeg(new Rect(0, 0, imgPreview.getWidth(), imgPreview.getHeight()), quality, jpegOutStream);
buffer = jpegOutStream.toByteArray();
fixJPEG = buffer;
fixTotalLength = buffer.length;
}
int lengthLeft = 0;
int offset = 0;
DatagramPacket dgpout;
if(fixImage==null || fixJPEG==null)
return;
/* Compress Optional */
if(true){
final YuvImage imgPreview = new YuvImage(fixImage, ImageFormat.NV21, WIDTH, HEIGHT, null);
ByteArrayOutputStream jpegOutStream = new ByteArrayOutputStream();
imgPreview.compressToJpeg(new Rect(0, 0, imgPreview.getWidth(), imgPreview.getHeight()), quality, jpegOutStream);
fixJPEG = jpegOutStream.toByteArray();
}
fixTotalLength = lengthLeft = fixJPEG.length;
// Send frame out. Split into packets of desired length.
try {
while(lengthLeft>packetSize){
dgpout= new DatagramPacket(fixJPEG, offset, packetSize);
offset+=packetSize;
lengthLeft-=packetSize;
dgpout.setAddress(InetAddress.getByName(IPAddress));
dgpout.setPort(PORT);
udpSocket.send(dgpout);
}
if(lengthLeft>0){
dgpout= new DatagramPacket(fixJPEG,offset,lengthLeft);
offset+=lengthLeft;
lengthLeft-=lengthLeft;
dgpout.setAddress(InetAddress.getByName(IPAddress));
dgpout.setPort(PORT);
udpSocket.send(dgpout);
}
Log.i(tag, "Sent Successfully: frame size="+fixTotalLength +"Buffer Size: "+udpSocket.getSendBufferSize());
} catch (UnknownHostException e1) {
Log.e(tag , "UnknownHostException. Sending failed.");
} catch (IOException e) {
Log.e(tag , "IOException. Sending failed.");
}
有没有人有提高效率的想法?这是否会在所有设备上发生?我正在运行Android 2.2。任何想法都表示赞赏。
*编辑添加了整个预览课程
答案 0 :(得分:2)
Atrix很可能(合法地)忽略了更改预览帧速率的请求。
在surfaceChanged
回调中,尝试调用以下内容并检查调试器中的值:
List<Integer> rates = parameters.getSupportedPreviewFrameRates();
Android文档说:
返回支持的预览帧速率列表。如果预览帧为null 不支持费率设置。
此外,setPreviewFrameRate()
的文件说:
这是目标帧速率。实际帧速率取决于 驱动程序。
因此,可能是Atrix不支持15 fps以外的任何其他内容进行预览。
无论如何,使用预览帧率的正确方法是先拨打getSupportedPreviewFrameRates()
。如果它返回非空结果,请在列表中找到您想要的最接近的数字,并将其传递给setPreviewFrameRate()
。否则,您将不得不忍受设备支持的任何速率。