MediaPlayer seekTo() doesn't update SurfaceView - java

I already saw a lot of questions regarding issues about Android's MediaPlayer, most of them because of the seekTo() function. Now I tried to use it, and it worked just as expected: badly!
This function seems to be very inconsistent, specially when we want to provide its functionality while the video is paused. In my case, I have videos of 30 to 60 frames and I want to play them one by one - without that delay that MediaMetadataRetriever.getFrameAtTime() provides.
The problem I'm facing is when I call seekTo(), it doesn't update the SurfaceView. It only works in the first time, after that the SurfaceView just stays the same, it never gets updated again.
I heard a rumor that seekTo() only works with a minimum interval of 1 second, but I tested with a longer video and seeking second by second didn't work either.
Code
mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
mSurfaceHolder.addCallback(this);
mMediaPlayer = new MediaPlayer();
mMediaPlayer.setDisplay(mSurfaceHolder);
mMediaPlayer.setOnSeekCompleteListener(new OnSeekCompleteListener() {
#Override
public void onSeekComplete(MediaPlayer mp) {
// Need this postDelayed(), otherwise the media player always
// returns 0 in getCurrentPosition(), don't know why...
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
mMediaPlayer.pause();
}
}, 100);
}
});
mMediaPlayer.setDataSource(localfile_source);
mMediaPlayer.prepare();
// Set the initial position.
mMediaPlayer.start();
mMediaPlayer.seekTo(500);
/**
We're assuming that targetMs is within 0 and the video's duration.
Also, targetMs is calculated to always move to the next/previous frame:
Example: currentMs + ( 1000 / framerate)
(if framerate = 20, then it will exist a frame in each 50ms)
*/
private void seekTo(int targetMs) {
mMediaPlayer.start();
mMediaPlayer.seekTo(targetMs);
}
Note that because of a known bug regarding using this function while the video is paused, is used a workaround:
Start the video;
Call seekTo();
Pause it on onSeekComplete().

From [this question]:
"You cannot do frame by frame seeking by using the MediaPlayer API's
provided by Android.
If you really want implement frame by frame seeking then you will have
to use a 3rd party multimedia framework like FFMPEG or you will need
to implement your own."
I created some test code to try it out anyway. I did not start() the video before using seekTo()—I just used seekTo() while paused.
When I moved forward in 50ms increments, I saw a series of about 4-5 frames repeat until roughly 2 seconds had elapsed; then, the set of preview frames changed to a new series of 4-5 frames. This seems to align with my previous trial wherein I moved forward in increments of 2000ms and saw a unique frame for each seekTo() call.
In summary, it appears that MediaPlayer picks several frames in each 2-second interval to be used as preview frames when the video is paused.

Related

Android copy built-in video recording quality and framerate using camera2

The image quality and the framerate I get when using the camera2 API does not match the one I get when I manually record a video using the camera app to a file.
I am trying to do real-time image processing using OpenCV on Android. I have manually recorded a video using the built-in camera application and everything worked perfectly: the image quality was good, the framerate was a stable 30 FPS.
My min SDK version is 22, so I am using the camera2 API's repeating requests. I have set it up, together with an ImageReader and the YUV_420_888 format. I have tried both the PREVIEW and the RECORD capture request templates, tried manually setting 18 capture request parameters in the builder (eg. disabling auto-white-balance, setting the color correction mode to fast), but the FPS was still around 8-9 and the image quality was poor as well. Another phone yielded the same results, despite its max. FPS being 16.67 (instead of 30).
The culprit is not my image processing (which happens in another thread, except for reading the image's buffer): I checked the FPS when I don't do anything with the frame (I didn't even display the image), it was still around 8-9.
You can see the relevant code for that here:
//constructor:
HandlerThread thread = new HandlerThread("MyApp:CameraCallbacks", Process.THREAD_PRIORITY_MORE_FAVORABLE);
thread.start();
captureCallbackHandler = new Handler(thread.getLooper());
//some UI event:
cameraManager.openCamera(cameraId, new CameraStateCallback()), null);
//CameraStateCallback#onOpened:
//size is 1280x720, same as the manually captured video's
imageReader = ImageReader.newInstance(size.getWidth(), size.getHeight(), ImageFormat.YUV_420_888, 1);
imageReader.setOnImageAvailableListener(new ImageAvailableListener(), captureCallbackHandler);
camera.createCaptureSession(Collections.singletonList(imageReader.getSurface()), new CaptureStateCallback(), captureCallbackHandler);
//CaptureStateCallback#onConfigured:
CaptureRequest.Builder builder = activeCamera.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
builder.addTarget(imageReader.getSurface());
//setting the FPS range has no effect: this phone only has one option
session.setRepeatingRequest(builder.build(), null, captureCallbackHandler);
//ImageAvailableListener#onImageAvailable:
long current = System.nanoTime();
deltaTime += (current - last - deltaTime) * 0.1;
Log.d("MyApp", "onImageAvailable FPS: " + (1000000000 / deltaTime));
//prints around 8.7
last = current;
try (Image image = reader.acquireLatestImage()) { }
On Samsung Galaxy J3 (2016), doing Camera.Parameters#setRecordingHint(true) (while using the deprecated camera API) achieves exactly what I wanted: the video quality and the framerate becomes the same as the built-in video recorder's. Unfortunately, it also means that I was unable to modify the resolution, and setting that hint did not achieve this same effect on a Doogee X5 MAX.

JavaFX MediaPlayer loop

I am working on a simpel game and I would like to have a video background. I first did this using a gif but this runs kind of slow. Now I created a video background using the mediaplayer and it works perfectly.
The video show without a problem.
The only problem I have is that the video does not want to loop. I tried every single aproache i found on the internet but nothing seems to work.
The video always plays 1 time and then stops.
I am using the java JDK8. Windows 10, 64 bit.
This is my code:
Media media = new
Media(getClass().getClassLoader().getResource("img/menu.mp4").toString());
MediaPlayer player = new MediaPlayer(media);
player.setAutoPlay(true);
player.setCycleCount(MediaPlayer.INDEFINITE);
MediaView view = new MediaView(player);
All of this is inside a stackpane.
I have tried exporting the mp4 to flv but this does not work.
If anyone knows different ways to create a video background, everything is welcome.
Edit:
So far no luck, I was thinking of using:
player.setOnEndOfMedia(new Runnable() {
#Override
public void run() {
player.seek(Duration.ZERO);
}
});
But not even this works..
Probably it's a bit late but you forgot to play after seeking to duration zero.
player.setOnEndOfMedia(new Runnable() {
#Override
public void run() {
player.seek(Duration.ZERO);
player.play();
}
});
It worked for me
From the MediaPlayer API:
Media playback commences at startTime and continues to stopTime. The interval defined by these two endpoints is termed a cycle with duration being the difference of the stop and start times. This cycle may be set to repeat a specific or indefinite number of times.
So you need to set startTime and stopTime before cycling works. For example (for a 5 second video):
player.setStartTime(Duration.seconds(0));
player.setStopTime(Duration.seconds(5));

Android4OpenCV: setting resolution at startup

I'm using Android4OpenCV to do some live image processing, and I'd like to use the smallest resolution the camera can offer. The default resolution is the largest the camera can offer.
I'm looking at the 3rd example, which allows the user to change resolutions via a menu. I'd like to modify that example to change the resolution at startup instead of requiring the user go through the menu. To do that, I simply add two lines to the otherwise empty onCameraViewStarted() function:
public void onCameraViewStarted(int width, int height) {
android.hardware.Camera.Size res = mOpenCvCameraView.getResolutionList().get(mOpenCvCameraView.getResolutionList().size()-1);
mOpenCvCameraView.setResolution(res);
}
And the thing is, this works perfectly fine on my Galaxy Nexus, running Android 4.2.2. The app starts up, and the resolution is set correctly.
However, when I run the exact same app on a Nexus 7 tablet, running Android 5.1, the app hangs on the call to setResolution(). Actually it works okay one time, but then hangs the second time you try to run it- even if you completely exit the app, remove it from the running apps, or restart the device. Other users are reporting the same error as well, so it's not just the Nexus 7 device- in fact, my Galaxy Nexus seems to be the only device where this works.
Specifically, the application goes into the setResolution() function, which then calls org.opencv.android.JavaCameraView.disconnectCamera(), which looks like this:
(Note: this code is internal to the OpenCV4Android library, this is not my code)
protected void disconnectCamera() {
/* 1. We need to stop thread which updating the frames
* 2. Stop camera and release it
*/
Log.d(TAG, "Disconnecting from camera");
try {
mStopThread = true;
Log.d(TAG, "Notify thread");
synchronized (this) {
this.notify();
}
Log.d(TAG, "Wating for thread");
if (mThread != null)
mThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
mThread = null;
}
/* Now release camera */
releaseCamera();
}
Looking at the logs, I can see that the thread gets stuck on the synchronized(this) line. The only other thing that synchronizes on that Object is the inner JavaCameraView.CameraWorker class, which is the mThread variable in the above code, started by the JavaCameraView class:
(Note: this code is internal to the OpenCV4Android library, this is not my code)
private class CameraWorker implements Runnable {
public void run() {
do {
synchronized (JavaCameraView.this) {
try {
JavaCameraView.this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (!mStopThread) {
if (!mFrameChain[mChainIdx].empty())
deliverAndDrawFrame(mCameraFrame[mChainIdx]);
mChainIdx = 1 - mChainIdx;
}
} while (!mStopThread);
Log.d(TAG, "Finish processing thread");
}
}
I've tried futzing with that code, changing the notify() to notifyAll(), and maintaining a List of CameraWorker threads and joining each one. But no matter what, the app still hangs at the disconnectCamera() call.
My questions are:
How can I modify the third OpenCV4Android example so that its resolution is set at startup?
What is causing the app to hang?
Why does this work on some devices but not others?
Edit: I haven't received any comments or answers, so I've crossposted to the OpenCV forums here.
Edit 2: As per cyriel's suggestion, I've tried setting the resolution after several frames have gone by:
int frames = 0;
public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
frames++;
if(frames == 6){
android.hardware.Camera.Size res = mOpenCvCameraView.getResolutionList().get(mOpenCvCameraView.getResolutionList().size()-1);
mOpenCvCameraView.setResolution(res);
}
return inputFrame.rgba();
}
However, now this gets stuck in the same exact place, even on my Galaxy Nexus, which works if I set the resolution in the onCameraViewStarted() function. I've tried increasing the frame count to 7 and even 100, but I always get stuck in the same place.
The most important question in your situation is whether it's working if you don't modify the code at all - are you able to change the resolution (via menu) without crashing the app?
If yes than the answer most likely is simple - it's the same bug in OpenCV as in Windows version: before changing camera resolution or fps (and most likely any property) you need to grab at least one (use 3-5 to be sure) frame before changing this property.
If no than most likely there is nothing you can do on your own - fill the bug report and wait for comments. The only alternative is to use other library to grab frames from camera and than convert it to OpenCV object.

How to keep monitoring the time without a while loop in Android

I am developing an app that will play certain sound at 9:15AM and 10:30AM in the morning,
I am done with playing the sound part already but I am facing issues while monitoring the current android system time.
Here is what I am doing,
I have an array where 9:15AM and 10:30AM are stored as String.
While I am using Java Calendar class to get current time of the System,
Calendar cTime = Calendar.getInstance();
public String currentTime() {
String hour = cTime.get(Calendar.HOUR) + "";
String mins = cTime.get(Calendar.MINUTE) + "";
return hour+""+mins+"AM";
}
and I am playing the sound using this code,
public void playSound() {
MediaPlayer player = MediaPlayer.create(this, R.raw.s);
player.start();
player.setOnCompletionListener(new OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
mp.release();
}
});
}
The problem is,
If it is 9:15AM in the morning and I run the app then the app would play the sound, or if it is 10:30 in the morning and I run the app then App will play the sound, but this isn't what I want,
I want my app to keep monitoring the time all the time and when its 9:15AM or 10:30AM, the sound should play regardless of the app is running or not.
i tried one approach by comparing the current time with 10:30AM or 9:30AM in a while loop, but it hangs my phone and this seems ugly idea.
I couldn't think of any other way to monitor the time, please suggest me something.
Use AlarmManager to schedule to get control at those points in time.
I think you should try these codes ( Android apis)
Use AlarmManager class to schedule and execute actions/operations that you want to perform regardless of the status of your android application whether it's running or not.
But before applying this concept you you should go through more details about this class , as it provides two types of alarm Elapsed Realtime and Real Time Clock..
and you will have to use BroadcastReceiver to receive the Intent .
if you want to use loops the you should use following codes(better than java calendar classes)
Time t = new Time();
t.setToNow();
or more better codes will be because now you will get current time zone of your user , so here you go;
Time t = new Time(Time.getCurrentTimezone());
t .setToNow();

How to use the accelerometer to detect virbration above or equal to 2Gs?

I'm doing android application which is something like a car "blackbox" which records the traveling process of the car.
But I'm face with the problem of how am i going to integrate an accelerometer which is capable of detecting movement (Probably >= 2Gs) when an accident occur then it should trigger the video recording to stop and saving it to the Archive file, thus not losing the file as a result of the accident.. Anyone knows how to do the above mention task, i'm rather needing urgent help here please! I've read android developer on accelerometer and its not helping in my situation here first i'm rather bad in physics second i'm new to android/java and my first attempt working with the accelerometer? Any simple solution? Thanks in Advance :)
This is part of the section of the video recording but now how am i going to incorporate accelerometer for "Auto-Archiving" purposes?
A couple of points:
The Bosch BMA150 is used in many smartphones with 2g set as the maximum acceleration value (so you might never see >2g).
With SENSOR_DELAY_FASTEST you can take readings about every 20 milliseconds on an HTC Desire. However, since you have a multi-tasking operating system on the phone, you cannot guarantee this timing (delays of a couple of seconds might occur when the operating system is busy).
Hence a smartphone is currently not really suitable for this application. If Android allows smarter use of accelerometers in future this could change. If onSensorChanged was allowed a threshold parameter then accelerations exceeding this threshold could be buffered in the accelerometer chip's memory and read out when appropriate.
Put your startRecording() method in the Activity below, it's called when acceleration exceeds 2G, you can change this by changing the value of CRASH_G_MULTIPLIER
public class YourActivity extends Activity {
private double G = 9.81;
private double CRASH_G_MULTIPLIER = 2;
#Override public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
startDropListening();
}
private void startRecording(){
// your code that does the recording here
}
private void startDropListening(){
SensorManager sm = (SensorManager)getSystemService(SENSOR_SERVICE);
sm.registerListener(
new SensorEventListener(){
#Override public void onAccuracyChanged(Sensor arg0, int arg1) {}
#Override public void onSensorChanged(SensorEvent arg0) {
double accel = Math.sqrt(
Math.pow(arg0.values[0], 2) +
Math.pow(arg0.values[1], 2) +
Math.pow(arg0.values[2], 2));
if (accel > G * CRASH_G_MULTIPLIER){
startRecording();
}
}
},
sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
SensorManager.SENSOR_DELAY_NORMAL);
}
}
There are some things you should be aware of:
As mentioned by others, the value of 2 needs to be increased quite substantially, but you can fine tune this yourself by experimentation.
You will want to acquire a wake lock. To create a wake lock, do this:
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
wl = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "ATag");
wl.acquire();
And the following when you are finished:
wl.release();
(You may want to change FULL_WAKE_LOCK to a lower priority wake lock). (You will also need to add the appropriate permission to your manifest file for the wake lock)
You may wish to increase the sample rate. This will drain battery significantly though. There are different values you can assign to it, in the code above, replace:
SensorManager.SENSOR_DELAY_NORMAL
with any of these:
SensorManager.SENSOR_DELAY_UI
SensorManager.SENSOR_DELAY_GAME
SensorManager.SENSOR_DELAY_FASTEST

Categories