手机网站开发教程pdf,wordpress 鼠标 效果,商城类网站怎么推广,设计logo理念在开源Android屏幕投屏代码scrcpy中#xff0c;使用了MediaCodec去获取和display关联的surface的内容#xff0c;再通过写fd的方式#xff08;socket等#xff09;传给PC端#xff0c;
MediaCodec的处理看起来比较清楚#xff0c;数据in和数据out 这里我们做另外一个尝试…在开源Android屏幕投屏代码scrcpy中使用了MediaCodec去获取和display关联的surface的内容再通过写fd的方式socket等传给PC端
MediaCodec的处理看起来比较清楚数据in和数据out 这里我们做另外一个尝试读取手机中的mp4文件显示到app的surface上来学习MediaCodec的使用。 code
import android.media.MediaCodec;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.os.Bundle;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import androidx.appcompat.app.AppCompatActivity;
import java.io.IOException;public class PlayActivity2 extends AppCompatActivity implements SurfaceHolder.Callback {private static final int REQUEST_PERMISSION 1;private static final String SAMPLE_MP4_FILE /sdcard/Download/test.mp4;private SurfaceView surfaceView;private MediaExtractor mediaExtractor;private MediaCodec mediaCodec;private boolean isPlaying false;private String TAG testPlay;Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_new);Log.i(TAG, onCreate);surfaceView findViewById(R.id.surfaceView);surfaceView.getHolder().addCallback(this);}Overrideprotected void onResume() {super.onResume();Log.i(TAG, onResume);if (!isPlaying) {Log.i(TAG, set isPlaying true);isPlaying true;// playVideo();}}Overrideprotected void onPause() {super.onPause();if (isPlaying) {Log.i(TAG, onPause);isPlaying false;releaseMediaCodec();}}Overridepublic void surfaceCreated(SurfaceHolder holder) {isPlaying true;Log.i(TAG, surfaceCreated);//需要另外启动一个线程去处理new Thread() {Overridepublic void run() {playVideo();}}.start();}Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {Log.i(TAG, surfaceChanged);}Overridepublic void surfaceDestroyed(SurfaceHolder holder) {Log.i(TAG, surfaceDestroyed);releaseMediaCodec();}private void playVideo() {try {Log.i(TAG, playVideo);mediaExtractor new MediaExtractor();mediaExtractor.setDataSource(SAMPLE_MP4_FILE);int videoTrackIndex getVideoTrackIndex();if (videoTrackIndex 0) {MediaFormat format mediaExtractor.getTrackFormat(videoTrackIndex);String mimeType format.getString(MediaFormat.KEY_MIME);mediaCodec MediaCodec.createDecoderByType(mimeType);Surface surface surfaceView.getHolder().getSurface();mediaCodec.configure(format, surface, null, 0);mediaCodec.start();Log.i(TAG, mediaCodec.start);decodeFrames(videoTrackIndex);}} catch (IOException e) {e.printStackTrace();}}private int getVideoTrackIndex() {for (int i 0; i mediaExtractor.getTrackCount(); i) {MediaFormat format mediaExtractor.getTrackFormat(i);String mime format.getString(MediaFormat.KEY_MIME);if (mime.startsWith(video/)) {mediaExtractor.selectTrack(i);return i;}}return -1;}private void decodeFrames(int videoTrackIndex) {boolean isEOS false;final int TIMEOUT_US 10000;while (!Thread.interrupted()) {if (!isPlaying)break;Log.i(TAG, decodeFrames, isPlaying isPlaying);int inputBufferIndex mediaCodec.dequeueInputBuffer(TIMEOUT_US);Log.i(TAG, inputBufferIndex inputBufferIndex);if (inputBufferIndex 0) {int sampleSize mediaExtractor.readSampleData(mediaCodec.getInputBuffer(inputBufferIndex), 0);if (sampleSize 0) {isEOS true;sampleSize 0;}long presentationTimeUs mediaExtractor.getSampleTime();mediaCodec.queueInputBuffer(inputBufferIndex, 0, sampleSize, presentationTimeUs, isEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);if (!isEOS) {Log.i(TAG, mediaExtractor.advance() sampleSize);mediaExtractor.advance();}}MediaCodec.BufferInfo bufferInfo new MediaCodec.BufferInfo();int outputBufferIndex mediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_US);Log.i(TAG, outputBufferIndex outputBufferIndex);if (outputBufferIndex 0) {mediaCodec.releaseOutputBuffer(outputBufferIndex, true);if ((bufferInfo.flags MediaCodec.BUFFER_FLAG_END_OF_STREAM) ! 0) {Log.i(TAG, inputBufferIndex, break);break;}}try {Thread.sleep(10);} catch (Exception e) {}}}private void releaseMediaCodec() {if (mediaCodec ! null) {mediaCodec.stop();mediaCodec.release();mediaCodec null;}if (mediaExtractor ! null) {mediaExtractor.release();mediaExtractor null;}}
}
注意这里的mp4文件放在了sdcard中需要获取读取权限
public void requestPermission() {if (Build.VERSION.SDK_INT 30) {if (!Environment.isExternalStorageManager()) {Intent intent new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);startActivity(intent);return;}} else {if (Build.VERSION.SDK_INT Build.VERSION_CODES.M) {if (PermissionChecker.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) ! PermissionChecker.PERMISSION_GRANTED) {requestPermissions(requestPermission, requestPermissionCode);}}}
} activity_new.xml里定义一个SurfaceView
?xml version1.0 encodingutf-8?
androidx.constraintlayout.widget.ConstraintLayout xmlns:androidhttp://schemas.android.com/apk/res/androidxmlns:apphttp://schemas.android.com/apk/res-autoxmlns:toolshttp://schemas.android.com/toolsandroid:layout_widthmatch_parentandroid:layout_heightmatch_parenttools:context.newActivitySurfaceViewandroid:idid/surfaceViewandroid:layout_widthmatch_parentandroid:layout_heightmatch_parent //androidx.constraintlayout.widget.ConstraintLayout playVideo的处理需要在另外一个线程中执行不能在主线程执行不然只能显示停止的一个画面。
01-28 18:14:19.388 21442 21442 I testPlay: set isPlaying true
01-28 18:14:19.431 21442 21442 I testPlay: surfaceCreated
01-28 18:14:19.431 21442 21442 I testPlay: playVideo
01-28 18:14:19.479 21442 21442 I testPlay: mediaCodec.start
01-28 18:14:19.479 21442 21442 I testPlay: decodeFrames, isPlayingtrue
01-28 18:14:19.480 21442 21442 I testPlay: inputBufferIndex2
01-28 18:14:19.483 21442 21442 I testPlay: mediaExtractor.advance()85878
01-28 18:14:19.493 21442 21442 I testPlay: outputBufferIndex-1
01-28 18:14:19.504 21442 21442 I testPlay: decodeFrames, isPlayingtrue
01-28 18:14:19.504 21442 21442 I testPlay: inputBufferIndex3
01-28 18:14:19.507 21442 21442 I testPlay: mediaExtractor.advance()3049 在上述代码中视频帧是通过 MediaCodec 解码后使用 Surface 对象在 SurfaceView 上进行渲染的。
以下代码片段展示了视频帧的渲染过程
MediaCodec.BufferInfo bufferInfo new MediaCodec.BufferInfo();
int outputBufferIndex mediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_US);
if (outputBufferIndex 0) {mediaCodec.releaseOutputBuffer(outputBufferIndex, true);if ((bufferInfo.flags MediaCodec.BUFFER_FLAG_END_OF_STREAM) ! 0) {break;}
}在每次循环中首先调用 dequeueOutputBuffer() 方法来获取可用的输出缓冲区的索引。如果返回的索引大于等于0则说明有可用的输出缓冲区。
然后通过调用 releaseOutputBuffer() 方法将输出缓冲区的索引传递给 MediaCodec通知它可以释放该缓冲区并将其渲染到指定的 Surface 上。
最后检查 BufferInfo 的 flags 标志如果标志中包含 BUFFER_FLAG_END_OF_STREAM则说明已经解码并渲染完整个视频帧序列可以退出循环。
在循环中不断解码和渲染视频帧就可以在 SurfaceView 上实时显示视频内容。 参考资料
Android MediaCodec解析-CSDN博客