From d422f68e4124e2dde01b4bb2d608047e3065eb98 Mon Sep 17 00:00:00 2001 From: Alexey Kuznetsov Date: Fri, 11 Mar 2016 20:42:34 +0300 Subject: [PATCH] m4a encoding --- .../activities/RecordingActivity.java | 25 ++-- .../activities/SettingsActivity.java | 10 +- .../audiorecorder/encoders/FileEncoder.java | 7 +- .../audiorecorder/encoders/FormatM4A.java | 112 ++++++++++++++++++ .../encoders/{Wav.java => FormatWAV.java} | 6 +- app/src/main/res/values/strings.xml | 4 +- app/src/main/res/xml/pref_general.xml | 2 +- 7 files changed, 150 insertions(+), 16 deletions(-) create mode 100755 app/src/main/java/com/github/axet/audiorecorder/encoders/FormatM4A.java rename app/src/main/java/com/github/axet/audiorecorder/encoders/{Wav.java => FormatWAV.java} (93%) diff --git a/app/src/main/java/com/github/axet/audiorecorder/activities/RecordingActivity.java b/app/src/main/java/com/github/axet/audiorecorder/activities/RecordingActivity.java index 0435da5..80e5ad8 100644 --- a/app/src/main/java/com/github/axet/audiorecorder/activities/RecordingActivity.java +++ b/app/src/main/java/com/github/axet/audiorecorder/activities/RecordingActivity.java @@ -37,16 +37,16 @@ import android.widget.Toast; import com.github.axet.audiorecorder.R; import com.github.axet.audiorecorder.app.MainApplication; import com.github.axet.audiorecorder.app.Storage; -import com.github.axet.audiorecorder.encoders.FileEncoder; +import com.github.axet.audiorecorder.encoders.Encoder; import com.github.axet.audiorecorder.encoders.EncoderInfo; -import com.github.axet.audiorecorder.encoders.Wav; +import com.github.axet.audiorecorder.encoders.FileEncoder; +import com.github.axet.audiorecorder.encoders.FormatM4A; +import com.github.axet.audiorecorder.encoders.FormatWAV; import com.github.axet.audiorecorder.widgets.PitchView; import java.io.DataOutputStream; import java.io.File; -import java.io.FileInputStream; import java.io.IOException; -import java.util.concurrent.atomic.AtomicLong; public class RecordingActivity extends AppCompatActivity { public static final String TAG = RecordingActivity.class.getSimpleName(); @@ -551,7 +551,19 @@ public class RecordingActivity extends AppCompatActivity { EncoderInfo info = getInfo(); - encoder = new FileEncoder(this, in, new Wav(info, out)); + Encoder e = null; + + SharedPreferences shared = PreferenceManager.getDefaultSharedPreferences(this); + String ext = shared.getString(MainApplication.PREFERENCE_ENCODING, ""); + + if (ext.equals("wav")) { + e = new FormatWAV(info, out); + } + if (ext.equals("m4a")) { + e = new FormatM4A(info, out); + } + + encoder = new FileEncoder(this, in, e); final ProgressDialog d = new ProgressDialog(this); d.setTitle("Encoding..."); @@ -564,8 +576,7 @@ public class RecordingActivity extends AppCompatActivity { encoder.run(new Runnable() { @Override public void run() { - int i = encoder.getProgress(); - d.setProgress(i); + d.setProgress(encoder.getProgress()); } }, new Runnable() { @Override diff --git a/app/src/main/java/com/github/axet/audiorecorder/activities/SettingsActivity.java b/app/src/main/java/com/github/axet/audiorecorder/activities/SettingsActivity.java index 04e69db..4629078 100644 --- a/app/src/main/java/com/github/axet/audiorecorder/activities/SettingsActivity.java +++ b/app/src/main/java/com/github/axet/audiorecorder/activities/SettingsActivity.java @@ -217,7 +217,15 @@ public class SettingsActivity extends AppCompatPreferenceActivity { bindPreferenceSummaryToValue(findPreference(MainApplication.PREFERENCE_STORAGE)); } - bindPreferenceSummaryToValue(findPreference(MainApplication.PREFERENCE_RATE)); + Preference rate = findPreference(MainApplication.PREFERENCE_RATE); + + if (Build.VERSION.SDK_INT < 21) { + getPreferenceScreen().removePreference(rate); + } else { + bindPreferenceSummaryToValue(rate); + } + + bindPreferenceSummaryToValue(findPreference(MainApplication.PREFERENCE_ENCODING)); } @Override diff --git a/app/src/main/java/com/github/axet/audiorecorder/encoders/FileEncoder.java b/app/src/main/java/com/github/axet/audiorecorder/encoders/FileEncoder.java index 886d13d..3db13e7 100644 --- a/app/src/main/java/com/github/axet/audiorecorder/encoders/FileEncoder.java +++ b/app/src/main/java/com/github/axet/audiorecorder/encoders/FileEncoder.java @@ -52,10 +52,10 @@ public class FileEncoder { int len = is.read(buf); if (len <= 0) { + Log.d("23", "end"); handler.post(done); return; - } - if (len > 0) { + } else { short[] shorts = new short[len / 2]; ByteBuffer.wrap(buf, 0, len).order(ByteOrder.BIG_ENDIAN).asShortBuffer().get(shorts); encoder.encode(shorts); @@ -65,10 +65,13 @@ public class FileEncoder { } } } + Log.d("23", "interrupted " + Thread.currentThread().isInterrupted()); } catch (IOException e) { + Log.d("23", "error " + e.getMessage()); t = e; handler.post(error); } finally { + Log.d("23", "close"); encoder.close(); if (is != null) { try { diff --git a/app/src/main/java/com/github/axet/audiorecorder/encoders/FormatM4A.java b/app/src/main/java/com/github/axet/audiorecorder/encoders/FormatM4A.java new file mode 100755 index 0000000..513c830 --- /dev/null +++ b/app/src/main/java/com/github/axet/audiorecorder/encoders/FormatM4A.java @@ -0,0 +1,112 @@ +package com.github.axet.audiorecorder.encoders; + +import android.annotation.TargetApi; +import android.media.MediaCodec; +import android.media.MediaCodecInfo; +import android.media.MediaFormat; +import android.media.MediaMuxer; + +import java.io.File; +import java.io.IOException; +import java.nio.ByteBuffer; + +@TargetApi(21) +public class FormatM4A implements Encoder { + EncoderInfo info; + MediaCodec encoder; + MediaMuxer muxer; + int audioTrackIndex; + long NumSamples; + + public FormatM4A() { + } + + public FormatM4A(EncoderInfo info, File out) { + this.info = info; + + MediaFormat format = new MediaFormat(); + format.setString(MediaFormat.KEY_MIME, "audio/mp4a-latm"); + format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectHE); + format.setInteger(MediaFormat.KEY_SAMPLE_RATE, info.sampleRate); + format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, info.channels); + format.setInteger(MediaFormat.KEY_BIT_RATE, 128 * 1024); + + try { + encoder = MediaCodec.createEncoderByType("audio/mp4a-latm"); + encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); + encoder.start(); + + muxer = new MediaMuxer(out.getAbsolutePath(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public void encode(short[] buf) { + int len = buf.length * 2; + int inputIndex = encoder.dequeueInputBuffer(-1); + if (inputIndex >= 0) { + ByteBuffer input = encoder.getInputBuffer(inputIndex); + input.clear(); + for (int i = 0; i < buf.length; i++) + input.putShort(buf[i]); + encoder.queueInputBuffer(inputIndex, 0, len, getCurrentTimeStamp(), 0); + } + + NumSamples += buf.length / info.channels; + + encode(); + } + + void encode() { + MediaCodec.BufferInfo outputInfo = new MediaCodec.BufferInfo(); + int outputIndex = encoder.dequeueOutputBuffer(outputInfo, 0); + if (outputIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { + audioTrackIndex = muxer.addTrack(encoder.getOutputFormat()); + muxer.start(); + } + + while (outputIndex >= 0) { + ByteBuffer output = encoder.getOutputBuffer(outputIndex); + output.position(outputInfo.offset); + output.limit(outputInfo.offset + outputInfo.size); + +// byte[] out = new byte[outputInfo.size]; +// output.get(out); + + muxer.writeSampleData(audioTrackIndex, output, outputInfo); + + encoder.releaseOutputBuffer(outputIndex, false); + outputIndex = encoder.dequeueOutputBuffer(outputInfo, 0); + } + } + + public void close() { + end(); + encode(); + + encoder.stop(); + encoder.release(); + + muxer.stop(); + muxer.release(); + } + + long getCurrentTimeStamp() { + return NumSamples * 1000 * 1000 / info.sampleRate; + } + + void end() { + int inputIndex = encoder.dequeueInputBuffer(-1); + if (inputIndex >= 0) { + ByteBuffer input = encoder.getInputBuffer(inputIndex); + input.clear(); + encoder.queueInputBuffer(inputIndex, 0, 0, getCurrentTimeStamp(), MediaCodec.BUFFER_FLAG_END_OF_STREAM); + } + } + + public EncoderInfo getInfo() { + return info; + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/github/axet/audiorecorder/encoders/Wav.java b/app/src/main/java/com/github/axet/audiorecorder/encoders/FormatWAV.java similarity index 93% rename from app/src/main/java/com/github/axet/audiorecorder/encoders/Wav.java rename to app/src/main/java/com/github/axet/audiorecorder/encoders/FormatWAV.java index cac5efd..04bf58c 100755 --- a/app/src/main/java/com/github/axet/audiorecorder/encoders/Wav.java +++ b/app/src/main/java/com/github/axet/audiorecorder/encoders/FormatWAV.java @@ -8,7 +8,7 @@ import java.io.*; import java.nio.ByteBuffer; import java.nio.ByteOrder; -public class Wav implements Encoder { +public class FormatWAV implements Encoder { int NumSamples; EncoderInfo info; int BytesPerSample; @@ -16,10 +16,10 @@ public class Wav implements Encoder { ByteOrder order = ByteOrder.LITTLE_ENDIAN; - public Wav() { + public FormatWAV() { } - public Wav(EncoderInfo info, File out) { + public FormatWAV(EncoderInfo info, File out) { this.info = info; NumSamples = 0; diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3078605..2aa6034 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -23,12 +23,12 @@ .wav (default) - .aac + .m4a wav - aac + m4a Audio Recorder diff --git a/app/src/main/res/xml/pref_general.xml b/app/src/main/res/xml/pref_general.xml index 9e72cce..ebbb6e0 100644 --- a/app/src/main/res/xml/pref_general.xml +++ b/app/src/main/res/xml/pref_general.xml @@ -22,7 +22,7 @@ android:key="encoding" android:negativeButtonText="@null" android:positiveButtonText="@null" - android:summary="Output file formats (.wav, .aac, ...)" + android:summary="Output file formats (.wav, .m4a, ...)" android:title="Encoding" />