Android JavaCV FFmpegFrameRecorder.stop() throws exception - android

I am using FFmpegFrameRecorder to capture preview frames from camera. I am using this setting:
mVideoRecorder = new FFmpegFrameRecorder(mVideoPath, 300, 300, 1);
mVideoRecorder.setFormat("mp4");
mVideoRecorder.setSampleRate(44100);
mVideoRecorder.setFrameRate(30);
mVideoRecorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
mVideoRecorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);
mVideoRecorder.setVideoQuality(0);
mVideoRecorder.setAudioQuality(0);
mVideoRecorder.setVideoBitrate(768000);
mVideoRecorder.setAudioBitrate(128000);
mVideoRecorder.setGopSize(1);
After I have finished capturing all frames by calling .record(IplImage) method I call mVideoRecorder.stop().
But from time to time stop() method throws
org.bytedeco.javacv.FrameRecorder$Exception: av_interleaved_write_frame() error -22 while writing interleaved video frame.
at org.bytedeco.javacv.FFmpegFrameRecorder.record(FFmpegFrameRecorder.java:727)
at org.bytedeco.javacv.FFmpegFrameRecorder.stop(FFmpegFrameRecorder.java:613)
I have not seen any regularity in this behaviour nor have been able to found what error -22 is. And after that no ffmpeg call on file in mVideoPath works (I guess that file is not even valid due that error).
I would really appreciate any help with this issue, thanks :)

Related

Phone crashes when calling mediaCodec.configure with error MediaCodec$CodecException: Error 0x80001001

The app I am working on gets the video from the camera through Surface and encodes it to video/avc (H264) I am doing that successfully and it is working great on phones like galaxy Note 10+ but on phones like Xiaomi note 10s which is a new phone I am having this issue. Here is what I am doing:
create format:
format = MediaFormat.createVideoFormat(
H264, videoWidth, videoHeight
).apply {
setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 0)
setInteger(MediaFormat.KEY_BIT_RATE, bitrate)
setInteger(MediaFormat.KEY_FRAME_RATE, videoFrameRate)
setInteger(
MediaFormat.KEY_COLOR_FORMAT,
CodecCapabilities.COLOR_FormatSurface
)
setFloat(MediaFormat.KEY_I_FRAME_INTERVAL, 1f)
}```
Then create encoderName:
val encoderName = MediaCodecList(
MediaCodecList.ALL_CODECS
).findEncoderForFormat(format) //using the format I shared in the first step
Then create:
codec = MediaCodec.createByCodecName(encoderName)
Then .setCallback(callback) //not important since we won't make it till this point, it will crash before that.
4. And this is the line where it crashes.
codec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE) //CRASH => MediaCodec$CodecException: Error 0x80001001
The rest
codec.setInputSurface(surface)
codec.start()
I am suspecting the
setInteger(
MediaFormat.KEY_COLOR_FORMAT,
CodecCapabilities.COLOR_FormatSurface
) //I tried changing the value and completely removing this setInteger, no luck :/
Error 0x80001001 also known as OMX_ErrorUndefined says:
"There was an error, but the cause of the error could not be determined".
Most likely cause for this error is insufficient resources. This can happen for example if you try to configure a hardware codec but there is not enough graphics memory available at the moment.
Suggestion 1: Make sure you release the codecs when you are done using them. You need to check all code paths.
Suggestion 2: Knowing that this can happen, you can filter the MediaCodecList keeping all the encoders that support the given format. Then wrap the configure() call in a try/catch block. And, if the call fails, try the next option from the list of codecs.
Note that on most devices there are at least two codecs for H264: a hardware codec and a software codec. The former one having better performance, the latter one being more resilient.

Android MediaCodec Format/Resolution Change mid-stream

I'm processing a live stream via MediaCodec and have a scenario where the MediaFormat changes mid-stream (ie: resolution of the video being decoded changes). Given I'm attaching the decoder to a Surface to render it as soon as I detect the change in resolution on the incoming stream I recreate the decoder before feeding it the new resolution buffer (providing it with the proper new MediaFormat).
I've been getting some weird errors which don't give me too much info as to what could be wrong, ie when calling MediaCodec.configure with the new format and same Surface:
android.media.MediaCodec$CodecException: Error 0xffffffea
at android.media.MediaCodec.native_configure(Native Method)
at android.media.MediaCodec.configure(MediaCodec.java:577)
Which when fetching the CodecException.getDiagnosticInfo it shows nothing that I can really use to understand the reason for the failure: android.media.MediaCodec.error_neg_22
I've also noted the following on the logs and found some related information and am wondering if there's something I need to do regarding the Surface itself (like detaching it from the old instance of the decoder being giving it to the new one):
07-09 15:00:17.217 E/BufferQueueProducer( 139): [SurfaceView] connect(P): already connected (cur=3 req=3)
07-09 15:00:17.217 E/MediaCodec( 5388): native_window_api_connect returned an error: Invalid argument (-22)
07-09 15:00:17.218 E/MediaCodec( 5388): configure failed with err 0xffffffea, resetting...
Looks like calling stop() and release() as well as reinitializing any references I had to the getInputBuffers() and getOutputBuffers() did the trick. At least I don't get the messages/exceptions anymore. Now I just need to figure out the Surface reference part as it seems the resized stream (when resolution changes) is still being fit in the original surface dimensions instead of adjusting the Surface for the new resolution.
If your encoder supports adaptive playback, then apparently you can alter some codec paramaters on the fly:
https://stackoverflow.com/a/34427724/1048170

MediaCodec encoding ignores my BUFFER_FLAG_SYNC_FRAME flag

In my Android application, I am encoding some media in webm (vp8) format using MediaCodec. The encoding is working as expected. However, I need to ensure that I create a sync frame once in a while. Here is what I do:
encoder.queueInputBuffer(..., MediaCodec.BUFFER_FLAG_SYNC_FRAME);
Later in the code, I check for sync frame:
encoder.dequeueOutputBuffer(bufferInfo, 0);
boolean isSyncFrame = (bufferInfo.flags & MediaCodec.BUFFER_FLAG_SYNC_FRAME);
The problem is that isSyncFrame never gets a true value.
I am wondering if I am making a mistake in my encoding configuration. May be there is a better way to tell the encoder to create a sync frame once in a while.
I hope it is not a bug in MediaCodec. Thank you in advance for your help.
There is no (current as of Android 4.3) way to request an on-demand sync frame using MediaCodec encoders. This is partly due to OMX, the underlying codec implementation in Android, that does not provide a way to specify which input frame should be encoded as a sync frame; although it has a way to trigger a sync frame "in the near future".
feisal's answer is the only currently supported way to control sync frames, but you have to do it at configuration time.
==edit re: jesup
You can trigger a sync frame in the near future using MediaCodec.setParameter:
Bundle params = new Bundle();
params.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0);
mCodec.setParameters(syncFrame);
Unfortunately, there is no (reliable) way to tell in MediaCodec if an encoded buffer is a sync frame other than doing it on your own by inspecting the byte-codes.
you can set the rate of I-frames in the MediaFormat object of your encoder by setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, int secs_between_iframes );

Android MediaCodec dequeOutputBuffer always returns -1

I am using the new MediaCodec API on Jelly Bean to decode an h264 stream.
Using the code snippets in the developer page , instantiated a decoder by name (taken from media_codec.xml), passed a surface and configured the codec.
The problem I am facing is, dequeOutputBuffer always returns -1.
Tried with a negative timeout to wait indefenitely, no luck with that.
Whenever I get a -1, refreshed the buffers using getOutputBuffers.
Please note that the same issue is seen when a custom app is used to parse the data from a media source and provide to decoder.
Any inputs on the above will be helpful
I had faced same problem. Incrementing presentationTimeUs parameter of queueInputBuffer() on each call solved the issue.
For example,
codec.queueInputBuffer(inputBufferIndex, 0, data.size, time, 0)
time += 66 //incrementing by 1 works too
If anyone else is facing this problem (as I did today) while starting with MediaCodec make sure to release the output codecs after you're done with them:
mediaCodec.releaseOutputBuffer(index, render);
or else the codec will run out of available buffers pretty soon.
It may be necessary to feed several input buffers before obtaining data in output buffer.
-1 is INFO_TRY_AGAIN_LATER, meaning the output buffer queue is still being prepared and you just need to call dequeueOutputBuffer again.
Try using a work loop that calls dequeueOutputBuffer in a loop similar to ExoPlayer:
while (drainOutputBuffer(positionUs, elapsedRealtimeUs)) {}
if (feedInputBuffer(true)) {
while (feedInputBuffer(false)) {}
}
where drainOutputBuffer is a method that calls dequeueOutputBuffer.

Sample 1 not ready - Soundpool in Android 2.1

guys i have a audio file which i am reading from sdcard it.On click of a button i am playing the audio file from sdcard.It successfully plays the audio file for 6 to 7 times but after that it shows unable to load (null) sample 1 not ready
i am working in Android 2.1 i.e., API 7.
String path="/sdcard/var/audio.mp3"
sound1 = mSoundPool.load(path,1);
mSoundPool.play(sound1, 1, 1, 1, time - 1, 1);
What is soundpool indicating me by saying unable to load (null) sample 1 not ready?
How can i fix this please help me i am struggling fro long time.
I have had similar problem with a wav file. Let's start discarding some things:
It is NOT a problem of waiting, although it may be in another cases. Please note that you have posted two DIFFERENT error messages like it were one:
unable to load (null)
sample 1 not ready
The first error is raised when you try to load the sample into the SoundPool, the second one when you try to play it. But in this case the second error is clearly a consequence of the first one: the sample could not be ready if it has not been loaded.
So you should concentrate before in the first error.
It is NOT related to MediaPlayer neither, since you are using the SoundPool that is a different thing AFAIK.
So the source of the problem may be, and should be discarded by order:
The file is not there.
The file is there, but it is not readable for some reason.
The file is there, and it is readable, but it is corrupt or not an audio file.
The file is there, and it is readable, and it is a non corrupted audio file, but SoundPool dislikes it.
This last was my case. For some reason SoundPool were unable to load a wav sample that in fact works in every other player that I have used. So I simply ended using another file with a different format. Here is the format of the offending file, as told by mplayer on my GNU/LiNUX box:
Opening audio decoder: [pcm] Uncompressed PCM audio decoder
AUDIO: 96000 Hz, 1 ch, s16le, 1536.0 kbit/100.00% (ratio: 192000->192000)
Selected audio codec: [pcm] afm: pcm (Uncompressed PCM)
And here it is the one for the other file that does work:
Opening audio decoder: [pcm] Uncompressed PCM audio decoder
AUDIO: 22050 Hz, 2 ch, s16le, 705.6 kbit/100.00% (ratio: 88200->88200)
Selected audio codec: [pcm] afm: pcm (Uncompressed PCM)
There are many differences, but the point is that the second works, so I simply discarded the first one and move into the second. Knowing this if I needed the first sound sample, I just need to adjust its rate and channels with an audio editor like Audacity to solve the problem.
Why it did not just work in first case? Who knows, but if I can solve it so easy... who cares at all?
Regards,
You will need to check file is loaded successfully before playing it using SoundPool.setOnLoadCompleteListener
public void loadSound (String strSound, int stream) {
boolean loaded = false;
mSoundPool.setOnLoadCompleteListener(new OnLoadCompleteListener() {
#Override
public void onLoadComplete(SoundPool soundPool, int sampleId,
int status) {
mSoundPool.play(stream, streamVolume, streamVolume, 1, LOOP_1_TIME, 1f);
}
});
try {
stream= mSoundPool.load(aMan.openFd(strSound), 1);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
I think that's your problem. Whole file is not loaded and you're trying to play it. You have to wait some time before playing it.
Have you tried calling mediaplayer.release() and re-initilizing? also http://developer.android.com/reference/android/media/MediaPlayer.html

Categories

Resources