• After 15+ years, we've made a big change: Android Forums is now Early Bird Club. Learn more here.

Apps Please, help with audio record troubles

I writing audio from microphone to mp4 (aac audio). I get audio raw data from Microphone by AudioRecord and send to MediaCodec. After that I send the audio data to MediaMux to get a mp4 file. I have problem: then my application record audio in background service and user does something with device (to navigate in menu or use other application), result audio record have troubles (faster playback, pauses). Sample audio file (before 0:15 of record I stay in application, after begin to navigate in menu):
https://www.dropbox.com/s/exkfbasal11fv08/test_1432724389482.mp4?dl=0

Code:
public void run() {
    if (mIsInitialized) {
        switch (type) {
            case ENCODE_AUDIO_FRAME:
                if (!mStopReceived) {
                    _offerAudioEncoder(mAudioData, presentationTimeNs);
                    processAudioFrame();
                }
                break;
        }
        // prevent multiple execution of same task
        mIsInitialized = false;
        mEncodingServiceQueueLength -= 1;
    } else {
        Log.e(TAG, "run() called but EncoderTask not initialized");
    }
}

private void _offerAudioEncoder(byte[] input, long presentationTimeNs) {
    if (audioBytesReceived == 0) {
        mAudioStartTime = presentationTimeNs;
    }

    mTotalInputAudioFrameCount++;
    audioBytesReceived += input.length;
    if (mEosSentToAudioEncoder && mStopReceived || input == null) {
        logStatistics();
        if (mEosReceived) {
            Log.d(TAG, "EOS received in offerAudioEncoder");
            closeAudioEncoder();
            mEosSentToAudioEncoder = true;
            if (!mStopReceived) {
                prepareAudioEncoder();
            } else {
                mEncodingService.shutdown();
            }
        }
        return;
    }
    // transfer previously encoded data to muxer
    drainEncoder(mAudioEncoder, mAudioBufferInfo, mAudioTrackIndex, false, "audio");

    try {
        ByteBuffer[] inputBuffers = mAudioEncoder.getInputBuffers();
        int inputBufferIndex = mAudioEncoder.dequeueInputBuffer(-1);
        if (inputBufferIndex >= 0) {
            ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
            inputBuffer.clear();
            inputBuffer.put(input);
            if (mAudioSoftwarePoller != null) {
                mAudioSoftwarePoller.recycleInputBuffer(input);
            }
            long presentationTimeUs = (presentationTimeNs - mAudioStartTime) / 1000; // in microseconds
            if (mEosReceived) {
                mAudioEncoder.queueInputBuffer(inputBufferIndex, 0, input.length, presentationTimeUs, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                closeAudioEncoder();
                mEosSentToAudioEncoder = true;
                if (mStopReceived) {
                    mEncodingService.shutdown();
                }
            } else {
                mAudioEncoder.queueInputBuffer(inputBufferIndex, 0, input.length, presentationTimeUs, 0);
            }
        }
    } catch (Throwable t) {
        t.printStackTrace();
    }
}

private void drainEncoder(MediaCodec encoder, MediaCodec.BufferInfo bufferInfo, TrackIndex trackIndex, boolean endOfStream, String type) {
    int TIMEOUT_USEC = 100;
    ByteBuffer[] encoderOutputBuffers = encoder.getOutputBuffers();
    while (true) {
        int encoderStatus = encoder.dequeueOutputBuffer(bufferInfo, TIMEOUT_USEC);
        if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
            if (!endOfStream) {
                if (VERBOSE) Log.d(TAG, "INFO_TRY_AGAIN_LATER " + type + " out of while");
                break;      // out of while
            } else {
                if (VERBOSE) Log.d(TAG, "no " + type + " output available, spinning to await EOS");
            }
        } else if (encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
            // not expected for an encoder
            if (VERBOSE) Log.d(TAG, "INFO_OUTPUT_BUFFERS_CHANGED " + type);
            encoderOutputBuffers = encoder.getOutputBuffers();

        } else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
            // should happen before receiving buffers, and should only happen once
            if (mMuxerStarted) {
                throw new RuntimeException("format changed after muxer start");
            }
            MediaFormat newFormat = encoder.getOutputFormat();
            Log.d(TAG, "encoder output format changed: " + newFormat + ".");

            // now that we have the Magic Goodies, start the muxer
            synchronized (mMuxer) {
                trackIndex.index = mMuxer.addTrack(newFormat);
                numTracksAdded++;
                Log.d(TAG, "Added " + type + " track index: " + trackIndex.index);
                if (numTracksAdded == TOTAL_NUM_TRACKS) {
                    mMuxer.start();
                    mMuxerStarted = true;
                    Log.d(TAG, numTracksAdded + " tracks added. Muxer started");
                    break;
                }
            }
        } else if (encoderStatus < 0) {
            Log.w(TAG, "unexpected result from " + type + " encoder.dequeueOutputBuffer: " +
                    encoderStatus);
        } else {
            if (encodedData == null) {
                if (VERBOSE) Log.d(TAG, "drainEncoder(" + endOfStream + ") " + type + " encodedData == null");
                throw new RuntimeException("encoderOutputBuffer " + encoderStatus +
                        " was null");
            }
            if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
                // The codec config data was pulled out and fed to the muxer when we got
                // the INFO_OUTPUT_FORMAT_CHANGED status.  Ignore it.
                if (VERBOSE) Log.d(TAG, "ignoring BUFFER_FLAG_CODEC_CONFIG");
                bufferInfo.size = 0;
            }
            if (bufferInfo.size != 0) {
                if (!mMuxerStarted) {
                    if (VERBOSE) Log.d(TAG, "drainEncoder(" + endOfStream + ") " + type + " Muxer not started");
                    throw new RuntimeException("muxer hasn't started");
                }
                // adjust the ByteBuffer values to match BufferInfo (not needed?)
                encodedData.position(bufferInfo.offset);
                encodedData.limit(bufferInfo.offset + bufferInfo.size);
                synchronized (mMuxer) {
                    mMuxer.writeSampleData(trackIndex.index, encodedData, bufferInfo);
                }
            }

            encoder.releaseOutputBuffer(encoderStatus, false);

            if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                if (!endOfStream) {
                    Log.w(TAG, "reached end of stream unexpectedly");
                } else {
                    if (VERBOSE) Log.d(TAG, "end of stream reached");
                }
                break;
            }
        }
    }
    long endTime = System.nanoTime();
}

public void processAudioFrame() {
    long audioPresentationTimeNs = System.nanoTime();
    byte[] thisBuffer;
    if (mDataBuffer.isEmpty()) {
        thisBuffer = new byte[mSamplesPerFrame];
    } else {
        thisBuffer = mDataBuffer.poll();
    }

    mReadResult = mAudioRecorder.read(thisBuffer, 0, mSamplesPerFrame);
    if (VERBOSE) Log.i(TAG, "FillBuffer       real: " + String.valueOf(mBufferWriteIndex)
            + " - " + String.valueOf(mBufferWriteIndex + mReadResult - 1));

    if (mReadResult != AudioRecord.ERROR_BAD_VALUE && mReadResult != AudioRecord.ERROR_INVALID_OPERATION) {
        mBufferWriteIndex = mBufferWriteIndex + mReadResult - 1;
        mTotalFramesWritten++;
        if (mAudioEncoder != null) {
            mEncodingService.submit(new EncoderTask(Encoder.this, EncoderTaskType.ENCODE_AUDIO_FRAME));
        }


        if (!mIsRecording && mAudioRecorder != null) {
            mAudioRecorder.setRecordPositionUpdateListener(null);
            mAudioRecorder.release();
            mAudioRecorder = null;
            Log.i(TAG, "stopped");
        }
    } else {
        Log.e(TAG, "Read error");
    }
}
 
Last edited:
Back
Top Bottom