首页 > 解决方案 > MediaRecorder 不会在 android 中录制双方的录音

问题描述

我无法在android中录制双方的音频。使用以下代码仅录制一侧音频。在android中可以进行双方录音吗?

公共类 CallRecordingService 扩展服务 {

private MediaPlayer mPlayer = null;
private MediaRecorder mRecorder = null;
private static final String LOG_TAG = CallRecordingService.class.getSimpleName();
private String savingPath;
boolean isRecordStarted = false;

@Nullable
@Override
public IBinder onBind(Intent intent) {
    return null;
}

@Override
public void onCreate() {
    super.onCreate();
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    super.onStartCommand(intent, flags, startId);
    savingPath = intent.getStringExtra("OUTPUT_PATH");
    AppApplication.getInstance().saveIntoPrefs(MConstants.RECORDING_FILE_PATH, savingPath);
    startRecording();
    return START_NOT_STICKY;
}

// this process must be done prior to the start of recording
private void resetRecorder() {
    mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
    mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
        mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
        mRecorder.setAudioEncodingBitRate(48000);
    } else {
        mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
        mRecorder.setAudioEncodingBitRate(64000);
    }
    mRecorder.setAudioSamplingRate(8000);
    mRecorder.setOutputFile(savingPath);

    try {
        mRecorder.prepare();
    } catch (IllegalStateException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

private void startRecording() {
    if (mRecorder != null) {
        mRecorder.stop();
        mRecorder.release();
    }
    mRecorder = new MediaRecorder();
    resetRecorder();
    try {
        // Sometimes prepare takes some time to complete
        Thread.sleep(2000);
        if (!isRecordStarted) {
            mRecorder.start();
            isRecordStarted = true;
        } else if (isRecordStarted) {
            isRecordStarted = false;
            stopRecording();
        }
    } catch (InterruptedException | IllegalStateException e) {
        e.printStackTrace();
    }
}

public void stopRecording() {
    if (mRecorder != null) {
        mRecorder.stop();
        mRecorder.reset();
        mRecorder.release();
        mRecorder = null;
    }
}

}

我坚持在我的应用程序中实现此录制功能。如果有人完成了这种实现,那么请解决我的错误。

标签: androidmediarecorder

解决方案


无障碍服务语音记录

摇篮

implementation 'pub.devrel:easypermissions:3.0.0'
implementation 'com.google.code.gson:gson:2.8.6'

清单.xml

<manifest>
    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>

    <!-- AccessibilityService -->
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

    <application>

        <!-- Accessibility Service -->
        <service android:name=".services.CallAccessibilityService"
            android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
            android:label="@string/accessibility_service_label">
            <intent-filter>
                <action android:name="android.accessibilityservice.AccessibilityService" />
            </intent-filter>
            <meta-data
                android:name="android.accessibilityservice"
                android:resource="@xml/accessibility_service_config" />
        </service>
<application>

res > xml > 可访问性_service_config

<?xml version="1.0" encoding="utf-8"?>
<accessibility-service
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:accessibilityEventTypes="typeAllMask"
    android:accessibilityFeedbackType="feedbackAllMask"
    android:accessibilityFlags="flagDefault"
    android:canRequestEnhancedWebAccessibility="true"
    android:notificationTimeout="100"
    android:packageNames="@null"
    android:canRetrieveWindowContent="true"
    android:canRequestTouchExplorationMode="true"
    />

主要活动

public class MainActivity extends AppCompatActivity implements  EasyPermissions.PermissionCallbacks {

    //Link: https://developer.android.com/guide/topics/media/mediarecorder?hl=en

    private String[] PERMISSIONS = {
            Manifest.permission.RECORD_AUDIO,
            Manifest.permission.MODIFY_AUDIO_SETTINGS
    };

    private static final int CODE_DRAW_OVER_OTHER_APP_PERMISSION = 2084;
    private static final int RC_APP_PERM = 124;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        // List of Permissions
        requestPermissions();
    }

    @AfterPermissionGranted(RC_APP_PERM)
    private void requestPermissions() {
        if (EasyPermissions.hasPermissions(this, PERMISSIONS)) {
            // Already have permission, do the thing
            onCheckPermissionOverOtherApp();
        } else {
            // Do not have permissions, request them now
            EasyPermissions.requestPermissions(this, "This app needs access to your camera and mic to make video calls", RC_APP_PERM, PERMISSIONS);
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
    }

    @Override
    public void onPermissionsGranted(int requestCode, @NonNull List<String> perms) {
        // Some permissions have been granted
        onCheckPermissionOverOtherApp();
        getInternalStorageFiles();
    }

    @Override
    public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) {
        // Some permissions have been denied
    }

    // On over other app | open the settings screen
    private void onCheckPermissionOverOtherApp() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {
            startActivityForResult(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())), CODE_DRAW_OVER_OTHER_APP_PERMISSION);
        } else {
            if (!isAccessibilitySettingsOn(getApplicationContext())) {
                startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS));
            }
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == RESULT_OK && requestCode == CODE_DRAW_OVER_OTHER_APP_PERMISSION) {
            if (!isAccessibilitySettingsOn(getApplicationContext())) {
                startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS));
            }
        } else {
            onCheckPermissionOverOtherApp();
            Toast.makeText(this,"Draw over other app permission not available. Closing the application", Toast.LENGTH_SHORT).show();
        }
    }

    // To check if service is enabled
    public boolean isAccessibilitySettingsOn(Context mContext) {
        int accessibilityEnabled = 0;
        final String service = mContext.getPackageName() + "/" + CallAccessibilityService.class.getCanonicalName();
        try {
            accessibilityEnabled = Settings.Secure.getInt(mContext.getApplicationContext().getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED);
            Log.v(TAG, "accessibilityEnabled = " + accessibilityEnabled);
        } catch (Settings.SettingNotFoundException e) {
            Log.e(TAG, "Error finding setting, default accessibility to not found: " + e.getMessage());
        }
        TextUtils.SimpleStringSplitter mStringColonSplitter = new TextUtils.SimpleStringSplitter(':');

        if (accessibilityEnabled == 1) {
            Log.v(TAG, "***ACCESSIBILITY IS ENABLED*** -----------------");
            String settingValue = Settings.Secure.getString(mContext.getApplicationContext().getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
            if (settingValue != null) {
                mStringColonSplitter.setString(settingValue);
                while (mStringColonSplitter.hasNext()) {
                    String accessibilityService = mStringColonSplitter.next();
                    Log.v(TAG, "-------------- > accessibilityService :: " + accessibilityService + " " + service);
                    if (accessibilityService.equalsIgnoreCase(service)) {
                        Log.v(TAG, "We've found the correct setting - accessibility is switched on!");
                        return true;
                    }
                }
            }
        } else {
            Log.v(TAG, "***ACCESSIBILITY IS DISABLED***");
        }
        return false;
    }


    private void getInternalStorageFiles() {
        //String path = Environment.getExternalStorageDirectory().toString() + "/Testing"; //getExternalFilesDir(), getExternalCacheDir(), or getExternalMediaDir()
        //String path = this.getApplicationContext().getFilesDir() + "/system_sound"; //file.getAbsolutePath()
        //String[] listOfFiles = Environment.getExternalStoragePublicDirectory (Environment.DIRECTORY_DOWNLOADS).list();

        String path = getApplicationContext().getFilesDir().getPath();
        String[] listOfFiles = getApplicationContext().getFilesDir().list();
        Log.d(TAG, "Files: " + new Gson().toJson(listOfFiles));
        if (listOfFiles != null) {
            for (String fileName : listOfFiles) {
                Log.d(TAG, "" + fileName +" | "+ path));
            }
        }
    }

}

布局 > action_bar.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <!--Root container-->
    <LinearLayout
        android:id="@+id/root_container"
        android:gravity="center"
        android:padding="10dp"
        android:orientation="vertical"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
        <ImageButton
            android:id="@+id/draggable_button"
            android:layout_width="45dp"
            android:layout_height="45dp"
            android:layout_marginBottom="5dp"
            android:backgroundTint="@color/colorDeepGrey"
            android:background="@drawable/ic_more_horiz_black_24dp" />
        <ImageButton
            android:id="@+id/btnStartRecording"
            android:layout_width="45dp"
            android:layout_height="45dp"
            android:layout_marginBottom="5dp"
            android:background="@drawable/shape_radius_100_stroke_grey_bg_white"
            android:src="@drawable/selector_play_button" />
        <ImageButton
            android:id="@+id/btnStopRecording"
            android:layout_width="45dp"
            android:layout_height="45dp"
            android:layout_marginBottom="5dp"
            android:background="@drawable/shape_radius_100_stroke_grey_bg_white"
            android:src="@drawable/selector_stop_button" />
        <ImageButton
            android:id="@+id/btnClose"
            android:layout_width="45dp"
            android:layout_height="45dp"
            android:background="@drawable/shape_radius_100_stroke_grey_bg_white"
            android:src="@drawable/selector_close_button" />
    </LinearLayout>

</FrameLayout>

呼叫无障碍服务

public class CallAccessibilityService extends AccessibilityService {

    private static final String TAG = "MyAccessibilityService";
    private FrameLayout mLayout;
    private boolean isStarted;
    private MediaRecorder mRecorder;

    private View mView;
    private WindowManager mWindowManager;

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {

    }

    @Override
    public void onInterrupt() {
    }

    @Override
    protected void onServiceConnected() {


        displayView();
    }

    private void displayView() {
        //Inflate the floating view layout we created
        mView = LayoutInflater.from(this).inflate(R.layout.action_bar, null);

        //Add the view to the window.
        final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.WRAP_CONTENT,
                (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) ? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY : WindowManager.LayoutParams.TYPE_PHONE,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,      // | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
                PixelFormat.TRANSLUCENT);

        //Specify the view position
        params.gravity = Gravity.TOP | Gravity.LEFT;        //Initially view will be added to top-left corner | Gravity.CENTER_VERTICAL|Gravity.END;
        params.x = 0;
        params.y = 100;

        //Add the view to the window
        mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
        if (mWindowManager != null) {
            mWindowManager.addView(mView, params);
        }

        ((ImageButton) mView.findViewById(R.id.btnClose)).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //Open the application  click.
                Intent intent = new Intent(CallAccessibilityService.this, MainActivity.class);
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                startActivity(intent);
                //close the service and remove view from the view hierarchy
                stopSelf();
            }
        });

        ((ImageButton) mView.findViewById(R.id.btnStartRecording)).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startRecording();
                Toast.makeText(CallAccessibilityService.this, "Playing", Toast.LENGTH_SHORT).show();
            }
        });
        ((ImageButton) mView.findViewById(R.id.btnStopRecording)).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                stopRecording();
                Toast.makeText(CallAccessibilityService.this, "Stopping", Toast.LENGTH_SHORT).show();
            }
        });

        //Drag and move floating view using user's touch action.
        ((ImageButton) mView.findViewById(R.id.draggable_button)).setOnTouchListener(new View.OnTouchListener() {
            private int initialX;
            private int initialY;
            private float initialTouchX;
            private float initialTouchY;

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                //params.x = (int) event.getX();
                //params.y = (int) event.getY();

                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        //remember the initial position.
                        initialX = params.x;
                        initialY = params.y;
                        //get the touch location
                        initialTouchX = event.getRawX();
                        initialTouchY = event.getRawY();
                        return true;
                    case MotionEvent.ACTION_UP:
                        int xDiff = (int) (event.getRawX() - initialTouchX);
                        int yDiff = (int) (event.getRawY() - initialTouchY);
                        //The check for Xdiff <10 && YDiff< 10 because sometime elements moves a little while clicking.
                        //So that is click event.
                        if (xDiff < 10 && yDiff < 10) {
                            Toast.makeText(CallAccessibilityService.this, "ACTION_UP", Toast.LENGTH_SHORT).show();
                        }
                        return true;
                    case MotionEvent.ACTION_MOVE:
                        //Calculate the X and Y coordinates of the view.
                        params.x = initialX + (int) (event.getRawX() - initialTouchX);
                        params.y = initialY + (int) (event.getRawY() - initialTouchY);
                        //Update the layout with new X & Y coordinate
                        mWindowManager.updateViewLayout(mView, params);
                        return true;
                }
                return false;
            }
        });
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (mView != null) mWindowManager.removeView(mView);
    }

    public void startRecording() {
        try {
            String path = getApplicationContext().getFilesDir().getPath();
            //String selectedPath = Environment.getExternalStorageDirectory() + "/Testing";
            //String selectedPath = Environment.getExternalStorageDirectory().getAbsolutePath() +"/Android/data/" + packageName + "/system_sound";

            File file = new File(path);
            if (!file.exists()){
                file.mkdirs();
            }

            mRecorder = new MediaRecorder();
            mRecorder.reset();


            //android.permission.RECORD_AUDIO
            String manufacturer = Build.MANUFACTURER;
            Log.d(TAG, manufacturer);
            mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); //MIC | VOICE_COMMUNICATION (Android 10 release) | VOICE_RECOGNITION | (VOICE_CALL = VOICE_UPLINK + VOICE_DOWNLINK)
            mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); //THREE_GPP | MPEG_4
            mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); //AMR_NB | AAC


            String mFilePath = file + "/" + "REC_" + new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()) + ".3gp"; //.3gp | .mp3
            mRecorder.setOutputFile(mFilePath);
            mRecorder.prepare();
            mRecorder.start();
            isStarted = true;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void stopRecording() {
        if (isStarted && mRecorder != null) {
            mRecorder.stop();
            mRecorder.reset(); // You can reuse the object by going back to setAudioSource() step
            mRecorder.release();
            mRecorder = null;
            isStarted = false;
        }
    }


    }


}

推荐阅读