Android Camera2 API من إبريق الشاي ، الجزء 2 ، كتابة فيديو



نواصل التعامل مع CAMERA2 Android API.
في المقالة السابقة ، أتقننا الكاميرا لالتقاط الصور باستخدام واجهة برمجة التطبيقات الجديدة. الآن دعنا نلتقط فيديو. بشكل عام ، في البداية ، كان هدفي الرئيسي هو بث الفيديو المباشر من كاميرا تعمل بنظام Android باستخدام Media Codec ، ولكن حدث ذلك في البداية ، ظهر Media Recorder على الساحة وأراد أن أشارك الجمهور الأكثر احتراماً بمدى جودة تسجيل مقاطع الفيديو. لذلك ، سوف نقوم بالبث في المرة القادمة ، لكن في الوقت الحالي سنكتشف كيفية إضافة "مسجل الوسائط" إلى واجهة برمجة التطبيقات الجديدة. تبين أن الموضوع المنشود عنه كان عاديًا ، لذا يمكن للمبتدئين وأقداح الشاي المثالية فقط النظر إلى القطة.



لذلك مسجل الوسائط



كما يمكننا أن نرى من نفس اسم الفصل والصورة أعلاه ، نحتاج إلى "مسجل الوسائط" لكي نأخذ مكانًا ما إلى مصدر الصوت أو الفيديو أو كلنا معًا ونقوم بالتسجيل في النهاية ، كل هذا في ملف بالتنسيق المرغوب فيه ، والأهم الوصول إليه.

في حالتنا ، المهمة بسيطة ، فنحن نأخذ الفيديو والصوت من الكاميرا والميكروفون ونكتب إلى ملف بتنسيق MPEG_4. بعض المنحرفين كانوا يستخدمون لإغلاق مأخذ توصيل شبكة لمسجل الوسائط بدلاً من ملف من أجل التمكن من قيادة الفيديو عبر الشبكة ، لكن لحسن الحظ ، كانت أوقات كهف كهذه موجودة بالفعل في الماضي. سنفعل نفس الشيء في المقالة التالية ، ولكن لهذا نأخذ برنامج الترميز Media المتحضر بالفعل.

كما يتذكر الجميع من الكاميرا API السابقة من عام 2011 ، ثم الاتصال MediaRecorder لم يكن صعبا. من الجيد ملاحظة أنه لا توجد صعوبة الآن. ودعونا لا نخاف من صورة المخطط الكامل للكاميرا.



نحتاج فقط إلى تثبيت "مسجل الوسائط" على سطح "السطح" الذي يتم عرض الصورة منه من الكاميرا ، وبعد ذلك سيفعل كل شيء بنفسه. مع الصوت ، يكون الأمر أكثر تافهة ، فقط قم بتعيين التنسيقات المطلوبة ، وسيقوم Media Recorder بتحديده من تلقاء نفسه دون إزعاجنا بجميع أنواع عمليات الاسترجاعات.

تذكر كيف فاجأ الصديق الياباني من آخر مشاركة:

أحد الأسباب التي تجعل Camera2 في حيرة هو عدد مرات معاودة الاتصال التي تحتاج إلى استخدامها لالتقاط طلقة واحدة.



وهنا ، على العكس من ذلك ، من المفاجئ قلة عدد عمليات الاسترجاعات اللازمة لتسجيل ملف فيديو. اثنين فقط. بينما تغني زيمفيرا: "أنا بحاجة إلى عمليات الاسترجاعات الخاصة بك على الأقل".

والآن سوف نكتبها

كمصدر ، نحن نأخذ الكود من المقال الأخير ونرمي كل ما يتعلق بالتصوير والرحيل ، في الواقع ، فقط دقة وتهيئة الكاميرا. نترك أيضا كاميرا واحدة فقط - الجبهة.

private CameraManager mCameraManager = null; private final int CAMERA1 = 0; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.d(LOG_TAG, " "); if (checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED || (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) || (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) ) { requestPermissions(new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO}, 1); } mCameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE); try { //      myCameras = new CameraService[mCameraManager.getCameraIdList().length]; for (String cameraID : mCameraManager.getCameraIdList()) { Log.i(LOG_TAG, "cameraID: " + cameraID); int id = Integer.parseInt(cameraID); //     myCameras[id] = new CameraService(mCameraManager, cameraID); } } catch (CameraAccessException e) { Log.e(LOG_TAG, e.getMessage()); e.printStackTrace(); } public class CameraService { private String mCameraID; private CameraDevice mCameraDevice = null; private CameraCaptureSession mSession; private CaptureRequest.Builder mPreviewBuilder; public CameraService(CameraManager cameraManager, String cameraID) { mCameraManager = cameraManager; mCameraID = cameraID; } private CameraDevice.StateCallback mCameraCallback = new CameraDevice.StateCallback() { @Override public void onOpened(CameraDevice camera) { mCameraDevice = camera; Log.i(LOG_TAG, "Open camera with id:" + mCameraDevice.getId()); //startCameraPreviewSession();        Media Recorder } @Override public void onDisconnected(CameraDevice camera) { mCameraDevice.close(); Log.i(LOG_TAG, "disconnect camera with id:" + mCameraDevice.getId()); mCameraDevice = null; } @Override public void onError(CameraDevice camera, int error) { Log.i(LOG_TAG, "error! camera id:" + camera.getId() + " error:" + error); } }; 

كما نرى ، تمت إضافة خيار RECORD_AUDIO إلى الأذونات. بدون ذلك ، يمكن لـ "مسجل الوسائط" تسجيل الفيديو العاري فقط دون صوت. وإذا كنا لا نزال نحاول تحديد تنسيقات الصوت دون إذن ، فلن يبدأ تشغيله على الإطلاق. لذلك ، نسمح بتسجيل الصوت والأشياء ، تذكر ، بالطبع ، أنه في الكود الحقيقي في التيار الرئيسي ، فإن القيام بهذه الأشياء ليس جيدًا ، لكنه جيد فقط في العرض التوضيحي.

بعد ذلك ، قم بتهيئة "مسجل الوسائط" نفسه بطريقة منفصلة

  private void setUpMediaRecorder() { mMediaRecorder = new MediaRecorder(); mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE); mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); mCurrentFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "test"+count+".mp4"); mMediaRecorder.setOutputFile(mCurrentFile.getAbsolutePath()); CamcorderProfile profile = CamcorderProfile.get(CamcorderProfile.QUALITY_480P); mMediaRecorder.setVideoFrameRate(profile.videoFrameRate); mMediaRecorder.setVideoSize(profile.videoFrameWidth, profile.videoFrameHeight); mMediaRecorder.setVideoEncodingBitRate(profile.videoBitRate); mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); mMediaRecorder.setAudioEncodingBitRate(profile.audioBitRate); mMediaRecorder.setAudioSamplingRate(profile.audioSampleRate); try { mMediaRecorder.prepare(); Log.i(LOG_TAG, "   "); } catch (Exception e) { Log.i(LOG_TAG, "   "); } } 


هنا ، أيضًا ، كل شيء واضح ومفهوم ولا يلزم تقديم تفسير.

بعد ذلك تأتي المرحلة الأكثر أهمية - إضافة مسجل الوسائط إلى Surface. في المشاركة الأخيرة ، عرضنا الصورة من الكاميرا على السطح وقمنا بتصوير إطار باستخدام قارئ الصور. للقيام بذلك ، حددنا ببساطة كلا المكونين في قائمة السطح.

 Arrays.asList(surface,mImageReader.getSurface()) 


هنا نفس الشيء ، فقط بدلاً من ImageReader نحدد:

 (Arrays.asList(surface, mMediaRecorder.getSurface()). 


هناك ، بشكل عام ، يمكنك نحت أي شيء بفاصلة ، وجميع المكونات التي تستخدمها وحتى Media Codec. أي أنه يمكنك التقاط الصور في نافذة واحدة ، وتصوير الفيديو ودفقه. سطح جيد - يسمح. صحيح ، هل من الممكن أن تفعل كل شيء في نفس الوقت؟ من الناحية النظرية ، يمكنك الحكم على صورة الكاميرا.




يجب أن ، مثل ، مجرد مبعثر عبر تيارات مختلفة. لذلك هناك مجال للتجارب.

ولكن العودة إلى وسائل الإعلام مسجل

تقريبا فعلنا كل شيء. على عكس التصوير الفوتوغرافي ، لسنا بحاجة إلى أي طلبات إضافية للتصوير ، لسنا بحاجة إلى أي مثيل لـ ImageSaver - مسجلنا المجتهد يقوم بكل شيء بمفرده. وانها لطيفة.

نتيجة لذلك ، يأخذ البرنامج نظرة بسيطة للغاية.

 package com.example.mediarecorder1; import androidx.appcompat.app.AppCompatActivity; import androidx.core.content.ContextCompat; import android.Manifest; import android.content.Context; import android.content.pm.PackageManager; import android.graphics.SurfaceTexture; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraDevice; import android.hardware.camera2.CameraManager; import android.hardware.camera2.CaptureRequest; import android.media.CamcorderProfile; import android.os.Bundle; import android.media.MediaRecorder; import android.os.Environment; import android.os.Handler; import android.os.HandlerThread; import android.util.Log; import android.view.Surface; import android.view.TextureView; import android.view.View; import android.widget.Button; import java.io.File; import java.util.Arrays; public class MainActivity extends AppCompatActivity { public static final String LOG_TAG = "myLogs"; CameraService[] myCameras = null; private CameraManager mCameraManager = null; private final int CAMERA1 = 0; private int count =1; private Button mButtonOpenCamera1 = null; private Button mButtonRecordVideo = null; private Button mButtonStopRecordVideo = null; public static TextureView mImageView = null; private HandlerThread mBackgroundThread; private Handler mBackgroundHandler = null; private File mCurrentFile; private MediaRecorder mMediaRecorder = null; private void startBackgroundThread() { mBackgroundThread = new HandlerThread("CameraBackground"); mBackgroundThread.start(); mBackgroundHandler = new Handler(mBackgroundThread.getLooper()); } private void stopBackgroundThread() { mBackgroundThread.quitSafely(); try { mBackgroundThread.join(); mBackgroundThread = null; mBackgroundHandler = null; } catch (InterruptedException e) { e.printStackTrace(); } } protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.d(LOG_TAG, " "); if (checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED || (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) || (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) ) { requestPermissions(new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO}, 1); } mButtonOpenCamera1 = findViewById(R.id.button1); mButtonRecordVideo = findViewById(R.id.button2); mButtonStopRecordVideo = findViewById(R.id.button3); mImageView = findViewById(R.id.textureView); mButtonOpenCamera1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (myCameras[CAMERA1] != null) { if (!myCameras[CAMERA1].isOpen()) myCameras[CAMERA1].openCamera(); } } }); mButtonRecordVideo.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if ((myCameras[CAMERA1] != null) & mMediaRecorder != null) { mMediaRecorder.start(); } } }); mButtonStopRecordVideo.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if ((myCameras[CAMERA1] != null) & (mMediaRecorder != null)) { myCameras[CAMERA1].stopRecordingVideo(); } } }); mCameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE); try { //      myCameras = new CameraService[mCameraManager.getCameraIdList().length]; for (String cameraID : mCameraManager.getCameraIdList()) { Log.i(LOG_TAG, "cameraID: " + cameraID); int id = Integer.parseInt(cameraID); //     myCameras[id] = new CameraService(mCameraManager, cameraID); } } catch (CameraAccessException e) { Log.e(LOG_TAG, e.getMessage()); e.printStackTrace(); } setUpMediaRecorder(); } private void setUpMediaRecorder() { mMediaRecorder = new MediaRecorder(); mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE); mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); mCurrentFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "test"+count+".mp4"); mMediaRecorder.setOutputFile(mCurrentFile.getAbsolutePath()); CamcorderProfile profile = CamcorderProfile.get(CamcorderProfile.QUALITY_480P); mMediaRecorder.setVideoFrameRate(profile.videoFrameRate); mMediaRecorder.setVideoSize(profile.videoFrameWidth, profile.videoFrameHeight); mMediaRecorder.setVideoEncodingBitRate(profile.videoBitRate); mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); mMediaRecorder.setAudioEncodingBitRate(profile.audioBitRate); mMediaRecorder.setAudioSamplingRate(profile.audioSampleRate); try { mMediaRecorder.prepare(); Log.i(LOG_TAG, "   "); } catch (Exception e) { Log.i(LOG_TAG, "   "); } } public class CameraService { private String mCameraID; private CameraDevice mCameraDevice = null; private CameraCaptureSession mSession; private CaptureRequest.Builder mPreviewBuilder; public CameraService(CameraManager cameraManager, String cameraID) { mCameraManager = cameraManager; mCameraID = cameraID; } private CameraDevice.StateCallback mCameraCallback = new CameraDevice.StateCallback() { @Override public void onOpened(CameraDevice camera) { mCameraDevice = camera; Log.i(LOG_TAG, "Open camera with id:" + mCameraDevice.getId()); startCameraPreviewSession(); } @Override public void onDisconnected(CameraDevice camera) { mCameraDevice.close(); Log.i(LOG_TAG, "disconnect camera with id:" + mCameraDevice.getId()); mCameraDevice = null; } @Override public void onError(CameraDevice camera, int error) { Log.i(LOG_TAG, "error! camera id:" + camera.getId() + " error:" + error); } }; private void startCameraPreviewSession() { SurfaceTexture texture = mImageView.getSurfaceTexture(); texture.setDefaultBufferSize(640, 480); Surface surface = new Surface(texture); try { mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD); /**Surface for the camera preview set up*/ mPreviewBuilder.addTarget(surface); /**MediaRecorder setup for surface*/ Surface recorderSurface = mMediaRecorder.getSurface(); mPreviewBuilder.addTarget(recorderSurface); mCameraDevice.createCaptureSession(Arrays.asList(surface, mMediaRecorder.getSurface()), new CameraCaptureSession.StateCallback() { @Override public void onConfigured(CameraCaptureSession session) { mSession = session; try { mSession.setRepeatingRequest(mPreviewBuilder.build(), null, mBackgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } @Override public void onConfigureFailed(CameraCaptureSession session) { } }, mBackgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } public void stopRecordingVideo() { try { mSession.stopRepeating(); mSession.abortCaptures(); mSession.close(); } catch (CameraAccessException e) { e.printStackTrace(); } mMediaRecorder.stop(); mMediaRecorder.release(); count++; setUpMediaRecorder(); startCameraPreviewSession(); } public boolean isOpen() { if (mCameraDevice == null) { return false; } else { return true; } } public void openCamera() { try { if (checkSelfPermission(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) { mCameraManager.openCamera(mCameraID, mCameraCallback, mBackgroundHandler); } } catch (CameraAccessException e) { Log.i(LOG_TAG, e.getMessage()); } } } @Override public void onPause() { stopBackgroundThread(); super.onPause(); } @Override public void onResume() { super.onResume(); startBackgroundThread(); } } 


إضافة إلى ذلك
 <?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <TextureView android:id="@+id/textureView" android:layout_width="356dp" android:layout_height="410dp" android:layout_marginTop="32dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.49" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <LinearLayout android:layout_width="292dp" android:layout_height="145dp" android:layout_marginStart="16dp" android:orientation="vertical" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/textureView" app:layout_constraintVertical_bias="0.537"> <Button android:id="@+id/button1" android:layout_width="match_parent" android:layout_height="wrap_content" android:text=" " /> <Button android:id="@+id/button2" android:layout_width="match_parent" android:layout_height="wrap_content" android:text=" " /> <Button android:id="@+id/button3" android:layout_width="match_parent" android:layout_height="wrap_content" android:text=" " /> </LinearLayout> </androidx.constraintlayout.widget.ConstraintLayout> 






وإضافة القليل إلى البيان

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


كل شيء يعمل بنجاح ويكتب الملفات.
الشيء الوحيد هو أنه لا توجد حماية من الخداع ، وبالتالي ، إذا كان من غير المعقول وضع أزرار على الشاشة بترتيب عشوائي ، فيمكنك كسر كل شيء.

Source: https://habr.com/ru/post/ar471774/


All Articles