How to prevent corrupted files? - android

I am making and recording application. And the big problem for me is that when user click the record button( i am using custom recorder, not default intent) and if they hit it again quickly or MediaRecorder is going to throw exception or video file will be corrupted. I tried to setEnabled() on buttons but that doesn't seems to work...i don't know why, it seems like they are not disabled for some reason...
Can somebody please help me with this?

It doesn't hurt to have more than one layer of validation. In addition to setEnabled(false) on your record button, try adding some logic to prevent a dirty write. Code assumes you're operating on a single thread.
boolean inProgress;
onClick()
{
inProgress = true;
record();
}
public boolean record()
{
if(!inProgress)
{
// record sound
// don't forget to reset inProgress if record success/fail
}
}

Related

how to access the phone call state in flutter?

I want to know whether the call is disconnected or continued, and based on that I want to perform an action in the application.
Can anyone tell me how to check if the phone call is disconnected or not?
along with that I also want to know if it is received by the end-user or not
any kind of help will be appreciated.
thank you
I think you should take the steps I list below:
One line of code can make a phone call
Wait for any in-flight phone
Calls Watch everything that happens on the phone during a single Call or all
calls.
Keep track of the length of calls, errors, and
call drops.
Now let’s start
Install the plugin
Flutter_phone_state: ^0.5.8
Initiate a call
It is best to make calls from your app whenever you can. This is the best way to find where the call came from.
final phoneCall = FlutterPhoneState.makePhoneCall("480-555-1234");
The truth about a call comes from a PhoneCall object.
showCallInfo(PhoneCall phoneCall) {
print(phoneCall.status);
print(phoneCall.isComplete);
print(phoneCall.events);
}
PhoneCall.events can be read as a stream, and when the call is over, the plugin will gracefully close the stream. The plugin keeps an eye on all calls in progress and will eventually force any call to time out.
watchEvents(PhoneCall phoneCall) {
phoneCall.eventStream.forEach((PhoneCallEvent event) {
print("Event $event");
});
print("Call is complete");
}
You could also just wait until the call is over.
waitForCompletion(PhoneCall phoneCall) async {
await phoneCall.done;
print("Call is completed");
}
Accessing in-flight calls
In-flight calls can be accessed like this:
final `activeCalls = FutterPhoneState.activeCalls;`
Note that activeCalls is a copy of the calls at the time you called it. This copy cannot be changed. It won't be updated on its own.
Watching all events
You can watch all the events instead of just focusing on one call. We recommend using “FlutterPhoneState.phoneCallEventStream” because it includes our own tracking logic, call timeouts, failures, etc.
watchAllPhoneCallEvents() {
FlutterPhoneState.phoneCallEvents.forEach((PhoneCallEvent event) {
final phoneCall = event.call;
print("Got an event $event");
});
print("That loop ^^ won't end");
}
You can sign up to get the raw events if you want to. Keep in mind that there are only so many of these events.
watchAllRawEvents() {
FlutterPhoneState.rawPhoneEvent.forEach((RawPhoneEvent event) {
final phoneCall = event.call;
print("Got an event $event");
});
print("That loop ^^ won't end");

Cordova media plugin - stop streaming not working - release() freezes the device

I am using cordova 6.4.0 with cordova-plugin-media for streaming radio-stations in an Android Application. Unfortunately there is a case, where the application is not responding properly anymore.
Let's say the user wants to stream a radiostation, but while the stream is loading, he wants to abort it (for example because the stream is down, or taking very long to load).
In this case I am not able to cancel the process!
media = new Media("http://direct.franceinfo.fr/live/franceinfo-midfi.mp3?ID=f9fbk29m84", mediaPlayerSuccess, mediaPlayerFail, mediaPlayerStatus);
media.play();
Now I want to cancel the process of buffering the stream, but I'm not able to. The functions:
media.pause();
media.stop();
are throwing error messages in the ADB-log and are calling the mediaPlayer-onError callback.
D/AudioPlayer( 3362): AudioPlayer Error: pausePlaying() called during invalid state: 1
...
D/AudioPlayer( 3362): AudioPlayer Error: stopPlaying() called during invalid state: 1
The media.release() command stops the loading of the stream! However just releasing the stream without stopping it, causes other, rather big problems:
Most of the times the system reacts just very slow and hangs a few seconds, if you call media.release() on a media-object. But if you do this often, the system completly freezes. Meaning it does not accetp remote-control commands anymore.
The Adb-log is still working, but does not show any errors in this case. Only the POWER-Button is still working (it locks and unlocks the screen). The only way to recover from this screwed-up state, is to reboot the device.
How am I supposed to cancel a Media-stream if it is not playing, yet? Is this a bug in the plugin?
Attached is the code-snippet, that I use to handle the media-streaming-logic. Like described above... it basically works, but it slows down or even freezes device, if you call it multiple times.
function radioControl(action, media_src){
//media_src is a webradio-streamurl.
if(action == 'play') {
// Initial Play
if(media === null){
mediaCreateObject(media_src);
}
// If we get PLAY but on antoher station
else if(media.src != media_src){
mediaReleaseRessources();
mediaCreateObject(media_src);
}
//interrupt_timer = false;
if(media === null){
mediaCreateObject(media_src);
}
media.play();
}
else if (action === 'pause') {
//If we get "pause", but it didn't even start yet
if(media._duration == -1){
mediaReleaseRessources();
}
else{
media.pause();
}
}
}
function mediaCreateObject(media_src){
media = new Media(media_src, mediaPlayerSuccess, mediaPlayerFail, mediaPlayerStatus);
}
function mediaReleaseRessources(){
media.release();
}
I found out, that this is not a cordova issue, but an 8 year-old (!) android-bug, that was never fixed. See here:
https://code.google.com/p/android/issues/detail?id=959
MediaPlayer "crash" (deadlocks the calling thread) when resetting or releasing an unused MediaPlayer
Basically the problem is: If you try to "release" a media-object that is not playing (yet), it will deadlock the calling thread, which causes the freezing that I have mentioned in the question. Unfortunately they never fixed this bug, but just marked it as "obsolete". In Android 5.1.1. the bug apparently is still there. Maybe they fixed it in later versions.
I have made a rather ugly workaround for this problem, but it is working. Basically what I did is:
We save every media-object in a javaScript-object. If the user stops it, while it plays, we can just stop and delete the object. But if it is not playing, we leave this media-object in this javaScript-object media_objects = {};
Also we save the currently active_media stream in a variable.
If cordova calls the mediaPlayerStatusChange-callback we loop through the media_objects and check if the status of one of the "pending"-objects has now changed to "running". - Cordova justs calls the media-status-change-callback without any indictation what media-object exactly just changed the state. That is unfortunate, so we have to check if one of the pending-"obsolete" objects now started playing. If so, we can stop and release it. (If the object is actually playing, stop and release works like intended - only if it's not playing, it causes the crash)
function mediaPlayerStatusChange(status){
mediaReleaseRessources();
// handle status change....
// ......
}
function mediaReleaseRessources(){
for(var key in media_objects) {
// We can only stop-and release an object, if it is playing
// If an object started playing, the "_duration"-value is != -1
if(key !== active_media && media_objects[key]._duration != -1) {
media_objects[key].stop();
media_objects[key].release();
delete media_objects[key];
}
}
}
This solution works for me, however I am still interested in a better and cleaner way to handle multiple media-streams in cordova.

Chromecast: Manage stop event (not pause) in receiver from sender Android

I have an important question: I manage stream flow on my receiver but I want to use stop message from my sender for stop the stream. Currently I'm using this sender (https://github.com/googlecast/CastVideos-android) and this receiver (https://github.com/googlecast/CastReferencePlayer) but the problem is that the sender sends me a command to pause and not a stop during the live.
In the documentation, side sender Android, I'm reading that the behavior is correct and the receiver must send a media status update message back to the sender and should report the state as MediaStatus.PLAYER_STATE_IDLE with reason MediaStatus.IDLE_REASON_CANCELLED.
Arrived at this point I want to modify the sample receiver and manage the pause like a stop and not like a pause. I want to manage the stop because when the sender click the stop button the receiver must stop the video and the sender must destroy "every player" (player, miniplayer, etc).
So My idea is this:
sampleplayer.CastPlayer.prototype.onPause_ = function() {
this.log_('onPause');
this.cancelDeferredPlay_('media is paused');
var isIdle = this.state_ === sampleplayer.State.IDLE;
var isDone = this.mediaElement_.currentTime === this.mediaElement_.duration;
var isUnderflow = this.player_ && this.player_.getState()['underflow'];
if (isUnderflow) {
this.log_('isUnderflow');
this.setState_(sampleplayer.State.BUFFERING, false);
this.mediaManager_.broadcastStatus(/* includeMedia */ false);
} else if (!isIdle && !isDone) {
this.setState_(sampleplayer.State.PAUSED, false);
} else if(this.isLiveStream) {
this.log_('onStop');
this.cancelDeferredPlay_('media is stopped');
var self = this;
sampleplayer.transition_(self.element_, sampleplayer.TRANSITION_DURATION_,
function() {
self.setState_(sampleplayer.State.IDLE, false);
self.resetMediaElement_();
self.mediaManager_.setIdleReason("CANCELLED");
});
return ;
}
this.updateProgress_();
};
As you can see in the third branc, I control a variable that I saved during the load of the video and after I set the state of player to IDLE, reset the mediaElement and finally I send to broadcast the State = IDLE and Reason = CANCELLED. In this way the sender see the message because I look the log but doesn't interpret this.
Now I don't know how to continue and manage this behaviour. Advice are welcome. Thank you.
Just to make sure my answer is covering your question, let me simplify your questions as the following: when playing a live stream, you want to still see the "stop" button on the sender side but if the user taps on that button, you want to stop the playback and unload the media.
If this is correct, you can achieve that from the sender side as well: CastVideos-android uses CastCompanionLibrary. There are two places that you need to update in that library: in VideoCastManager#togglePlayback() and VideoCastControllerFragment#togglePlayback(). In the former place, you need to update the conditional there to read like the following:
if (isPlaying && isRemoteStreamLive()) {
stop();
} else if (isPlaying) {
pause();
} else {
... //leave as is
}
In the latter case, you need to update one of the switch statements:
case MediaStatus.PLAYER_STATE_PLAYING:
if (mSelectedMedia.getStreamType() == MediaInfo.STREAM_TYPE_LIVE) {
mCastManager.stop();
mPlaybackState = MediaStatus.PLAYER_STATE_IDLE;
} else {
mCastManager.pause();
mPlaybackState = MediaStatus.PLAYER_STATE_BUFFERING;
}
break;
Basically, you are adding a logic that if the content is playing remotely, "toggling playback" should call stop() rather than pause() when dealing with a live stream.
Note that calling stop() means you are completely unloading your media and you may need to do some additional work to play another media but I haven't tested that since I don't call stop() in the CastVideos app. Also note that I have not tested/tried the above suggestion since I don't have a live stream to play with but I am hoping it would work.

State Machine inside Android - Stop onActivityResult breaking game while loop flow

I am trying to implement a State Machine into my Android game (note that it is not a game that needs to be constantly redrawn with a UI as it just works using standard Android Activity structure). I have found this example below of how you can implement a State Machine with a switch statement:
main() {
while(true) {
collectInput(); // deal with common code for filling in keyboard queues, determining mouse positions, etc.
preBookKeeping(); // do any other work that needs constant ticks, like updating streaming/sound/physics/networking receives, etc.
runLogic(); // see below
postBookKeeping(); // again any stuff that needs ticking, but would want to take into account what happened in runLogic this frame, e.g. animation/particles/networking transmit, etc
drawEverything(); // any actual rendering actions you need to take
}
}
runLogic() {
// this is where you actually have a state machine
switch (state) {
case WaitingForInput:
// look at the collected input and see if any of it is actionable
case WaitingForOpponent:
// look at the input and warn the player that they are doing stuff that isn't going to work right now e.g. a "It's not your turn!" notification.
// otherwise, use input to do things that might be valid when it's not the player's turn, like pan around the map.
case etc:
// a real game would have a ton more actual states, including transition states, start/end/options screens, etc.
}
}
Whilst transitioning my game from the loop below to the State Machine, I am having issues. If say from this main game Activity, I launch another Activity in order to ask the player a question (happens inside playTurn()), I will then obviously utilise onActivityResult() and return the player's answer to the main game Activity. How should I handle the return of the player's answer and allow the code to then continue running in playTurn(), inside the main playGame() loop, without breaking the while loop flow? The only way I actually can figure out is by utilising a while loop inside playTurn() that simply keeps looping whilst the answer is 0 but that seems horrifically wasteful. Thanks in advance, any help is appreciated!
public void playGame() {
initialise();
boolean finished = false;
while (!finished) {
playTurn();
// Check if there is a winner after each turn is played
boolean winner = winner();
if (winner) {
finished = true;
}
}
}

How to know when MediaRecorder has finished writing data to file

We're using MediaRecorder to record video to a file on the external storage using setOutputFile() before doing the actual recording.
Everything works fine, but the main issue is that as soon as the recording is done, we want to start playing the recorded video back in a VideoView.
How to know when the file is ready to be read and played back?
The FileObserver class suits your needs perfectly. Here is the documentation. It's easy to use. When a observed file is closed after writing, the onEvent callback is called with CLOSE_WRITE as the parameter.
MyFileObserver fb = new MyFileObserver(mediaFile_path, FileObserver.CLOSE_WRITE);
fb.startWatching();
class MyFileObserver extends FileObserver {
public MyFileObserver (String path, int mask) {
super(path, mask);
}
public void onEvent(int event, String path) {
// start playing
}
}
Don't forget to call stopWatching().
We solved similar problem with the following algo:
while (file not complete)
sleep for 1 sec
read the fourth byte of the file
if it is not 0 (contains 'f' of the 'ftyp' header) then
file is complete, break
The key point is that MediaRecorder writes the ftyp box at the very last moment. If it is in place, then the file is complete.
In my tests irrespective of the size of the recording mediaRecorder.stop() is a blocking method that only returns after the file has been completely written and closed by the media recorder.
So JPMs answer is actually correct.
You can verify this by calling File.length() immediately after stop(). You will find that the output file length is the final length of the file at this point. In other words media recorder does not write anything further to the file after stop() has returned.
I haven't tried this myself but this might work:
public void release () Since: API Level 1
Releases resources associated with this MediaRecorder object. It is
good practice to call this method when you're done using the
MediaRecorder.
If it does what it says, then I guess if you call this and after this method returns you know the file is ready.
Apparently there is no way to detect when the recording has stopped in Media player, but there is a stop() that you can override if you create a custom class that implements MediaRecorder. here I would do something like this:
public class MyRecorder implements MediaRecorder {
public boolean stopped;
.... implement all the methods that MediaRecorder has making
sure to call super for each method.
#Override
public void myStop() {
this.stopped = true;
super.stop();
}
}
Then you can access the boolean to see if it has stopped recording.
A dirty way would be to check the lastModified() value of the File and open the VideoView if the File wasn't modified for 2 seconds.

Categories

Resources