网站建设用处,wordpress+搬瓦工迁移,怎样制作一个购物小程序,企业管理网站模板说明#xff1a; 此MediaCodec解码H264实操主要针对Android12.0系统。通过读取sd卡上的H264码流Me获取视频数据#xff0c;将数据通过mediacodec解码输出到surfaceview上。 1 H264码流和MediaCodec解码简介
1.1 H264码流简介
H.264#xff0c;也被称为MPEG-4 AVC#xff…说明 此MediaCodec解码H264实操主要针对Android12.0系统。通过读取sd卡上的H264码流Me获取视频数据将数据通过mediacodec解码输出到surfaceview上。 1 H264码流和MediaCodec解码简介
1.1 H264码流简介
H.264也被称为MPEG-4 AVCAdvanced Video Coding是一种广泛使用的数字视频压缩标准主要用于视频编码。H.264标准由ITU-T视频编码专家组VCEG和ISO/IEC动态图像专家组MPEG共同开发旨在提供比之前的视频编码标准更高的数据压缩效率。
H.264是一种基于块的编码技术它将视频帧分为多个宏块MacroblocksMBs每个宏块包含亮度信息和色度信息。
关于H264码流相关概念还有
帧类型包括I、P、B三种类型说明如下
I帧Intra-coded frames关键帧不依赖其他帧进行解码包含完整的图像信息。P帧Predictive-coded frames预测帧依赖前一个I帧或P帧进行解码包含相对于前一帧的差分信息。B帧Bidirectional predictive-coded frames双向预测帧依赖前后两个帧进行解码用于提高压缩效率。
编码过程包括帧内预测Intra prediction、帧间预测Inter prediction、变换Transform、量化Quantization和熵编码Entropy coding等步骤。
码流结构H.264码流由一系列的NAL单元Network Abstraction Layer Units组成每个NAL单元包含一个头部和数据负载头部定义了负载的类型和重要性。
等等概念想要有更多了解可查看以下文章持续更新中
系统化学习 H264视频编码01基础概念 系统化学习 H264视频编码02 I帧 P帧 B帧 引入及相关概念解读
系统化学习 H264视频编码03数据压缩流程及相关概念
。。。
1.2 MediaCodec解码说明
MediaCodec 是 Android 提供的一个音视频编解码器类允许应用程序对音频和视频数据进行编码压缩和解码解压缩。它在 Android 4.1API 级别 16版本中引入广泛应用于处理音视频数据如播放视频、录制音频等。
以下是 MediaCodec 解码的基本步骤 创建 MediaCodec 实例通过调用 MediaCodec.createDecoderByType 方法并传入解码类型如 video/avc 或 audio/mp4a-latm来创建解码器。 配置解码参数通过调用 configure 方法配置解码器传入解码参数如解码格式、输出格式等。 准备输出 Surface为解码器准备输出 Surface。输出 Surface 用于接收解码后的数据并显示在屏幕上。 开始解码调用 start 方法启动解码器。 发送输入数据将待解码的数据通过 write 方法发送到解码器的输入队列。 处理输出数据监听输出队列通过 dequeueOutputBuffer 方法获取解码后的数据并将其显示在屏幕上。 停止解码解码完成后调用 stop 方法停止解码器。 释放资源调用 release 方法释放解码器资源。
通过这些步骤应用程序可以实现对视频和音频数据的高效编解码处理。针对本工程主要通过从sd卡上读取h264码流通过mediacodec解码视频并播放到surfaceview上。
2 MediaCodec解码H264码流代码完整解读(android Q)
2.1 关于权限部分的处理
关于权限需要在AndroidManifest.xml中添加权限具体如下所示
uses-permission android:nameandroid.permission.MANAGE_EXTERNAL_STORAGEtools:ignoreScopedStorage /
uses-permission android:nameandroid.permission.READ_EXTERNAL_STORAGEtools:ignoreScopedStorage /
uses-permission android:nameandroid.permission.WRITE_EXTERNAL_STORAGEtools:ignoreScopedStorage /
关于运行时权限的请求等这里给出一个工具类参考代码具体如下所示
public class Permission {public static final int REQUEST_MANAGE_EXTERNAL_STORAGE 1;//需要申请权限的数组private static final String[] permissions {Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.CAMERA};//保存真正需要去申请的权限private static final ListString permissionList new ArrayList();public static int RequestCode 100;public static void requestManageExternalStoragePermission(Context context, Activity activity) {if (!Environment.isExternalStorageManager()) {showManageExternalStorageDialog(activity);}}private static void showManageExternalStorageDialog(Activity activity) {AlertDialog dialog new AlertDialog.Builder(activity).setTitle(权限请求).setMessage(请开启文件访问权限否则应用将无法正常使用。).setNegativeButton(取消, null).setPositiveButton(确定, (dialogInterface, i) - {Intent intent new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);activity.startActivityForResult(intent, REQUEST_MANAGE_EXTERNAL_STORAGE);}).create();dialog.show();}public static void checkPermissions(Activity activity) {for (String permission : permissions) {if (ContextCompat.checkSelfPermission(activity, permission) ! PackageManager.PERMISSION_GRANTED) {permissionList.add(permission);}}if (!permissionList.isEmpty()) {requestPermission(activity);}}public static void requestPermission(Activity activity) {ActivityCompat.requestPermissions(activity,permissionList.toArray(new String[0]),RequestCode);}
}
这样如果后面又更多的权限都可以使用该方法来处理处理方式为
Permission.checkPermissions(this);
Permission.requestManageExternalStoragePermission(getApplicationContext(), this);
2.2 解码的处理
关于解码部分主要是MediaCodec的初始化、解码处理部分代码如下所示
public class H264Decoder implements Runnable {private final String path;private final String TAG H264Decoder;MediaCodec mediaCodec;boolean enablePlay false;public H264Decoder(String path, Surface surface, int width , int height) {this.path path;try {mediaCodec MediaCodec.createDecoderByType(video/avc);MediaFormat mediaformat MediaFormat.createVideoFormat(video/avc, width, height);mediaformat.setInteger(MediaFormat.KEY_FRAME_RATE, 15);mediaCodec.configure(mediaformat, surface, null, 0);} catch (IOException e) {throw new RuntimeException(e);}}public void play() {enablePlay true;mediaCodec.start();new Thread(this).start();}public void stop(){enablePlay false;}Overridepublic void run() {try {byte[] bytes null;try {//注意这里是从文件中一次性读H264取码流数据因此不适合特别大的视频bytes getBytes(path);} catch (Exception e) {throw new RuntimeException(e);}int startIndex 0;MediaCodec.BufferInfo info new MediaCodec.BufferInfo();while (enablePlay) {int nextFrameStart findByFrame(bytes, startIndex5, bytes.length);//MediaCodec输入缓冲区操作int inIndex mediaCodec.dequeueInputBuffer(10000);if (inIndex 0) {ByteBuffer byteBuffer mediaCodec.getInputBuffer(inIndex);int length nextFrameStart - startIndex;byteBuffer.put(bytes, startIndex, length);mediaCodec.queueInputBuffer(inIndex, 0, length, 0, 0);startIndex nextFrameStart;}//MediaCodec输出缓冲区操作int outIndex mediaCodec.dequeueOutputBuffer(info,10000);if (outIndex 0) {try {//这里延迟下避免刷的过快Thread.sleep(40);} catch (InterruptedException e) {throw new RuntimeException(e);}mediaCodec.releaseOutputBuffer(outIndex, true);}}} catch (Exception e) {Log.i(TAG, run decoder error:e.toString());}}private int findByFrame( byte[] bytes, int start, int totalSize) {for (int i start; i totalSize-4; i) {//这里是一帧的结束符 00 00 00 01 或者 00 00 01if (((bytes[i] 0x00) (bytes[i 1] 0x00) (bytes[i 2] 0x00) (bytes[i 3] 0x01))||((bytes[i] 0x00) (bytes[i 1] 0x00) (bytes[i 2] 0x01))) {return i;}}return -1;}public byte[] getBytes(String path) throws IOException {InputStream is new DataInputStream(Files.newInputStream(new File(path).toPath()));int len;int size 1024;byte[] buf;ByteArrayOutputStream bos new ByteArrayOutputStream();buf new byte[size];while ((len is.read(buf, 0, size)) ! -1)bos.write(buf, 0, len);buf bos.toByteArray();return buf;}
}
2.3 主流程代码参考实现
这里以 H264decoderActivity 为例给出一个MediaCodec解码功能代码的参考实现。具体实现如下
public class H264decoderActivity extends AppCompatActivity {H264Decoder h264Decoder;private final String TAG MainActivity;Context mContext;Surface surface;private boolean isPlaying false; // 用于跟踪播放状态Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);EdgeToEdge.enable(this);mContext this;setContentView(R.layout.h264_decode_activity_main);ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) - {Insets systemBars insets.getInsets(WindowInsetsCompat.Type.systemBars());v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);return insets;});initSurface();Permission.checkPermissions(this);Permission.requestManageExternalStoragePermission(getApplicationContext(), this);Button playButton findViewById(R.id.button);playButton.setOnClickListener(new View.OnClickListener() {Overridepublic void onClick(View view) {// 切换播放状态isPlaying !isPlaying;// 根据播放状态更新按钮文本if (isPlaying) {playButton.setText(R.string.stopplay);//Environment.DIRECTORY_DOWNLOADS), ags/out.h264).getAbsolutePath(),h264Decoder new H264Decoder(new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), ags/outputtest4.h264).getAbsolutePath(),surface,1280,720);h264Decoder.play();} else {playButton.setText(R.string.startplay);h264Decoder.stop();}}});}private void initSurface() {SurfaceView mSurface findViewById(R.id.preview);mSurface.getHolder().addCallback(new SurfaceHolder.Callback() {Overridepublic void surfaceCreated(NonNull SurfaceHolder surfaceHolder) {Log.d(TAG,surfaceCreated);surfacesurfaceHolder.getSurface();}Overridepublic void surfaceChanged(NonNull SurfaceHolder surfaceHolder, int i, int i1, int i2) {Log.d(TAG,surfaceChanged);}Overridepublic void surfaceDestroyed(NonNull SurfaceHolder surfaceHolder) {Log.d(TAG,surfaceDestroyed);}});}
}
这里涉及的layout布局文件内容如下
?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:idid/mainandroid:layout_widthmatch_parentandroid:layout_heightmatch_parenttools:context.MainActivitySurfaceViewandroid:idid/previewandroid:layout_width372dpandroid:layout_height240dpandroid:visibilityvisibleapp:layout_constraintBottom_toBottomOfparentapp:layout_constraintLeft_toLeftOfparentapp:layout_constraintRight_toRightOfparentapp:layout_constraintTop_toTopOfparent /Buttonandroid:idid/buttonandroid:layout_widthwrap_contentandroid:layout_heightwrap_contentandroid:textstring/playtestapp:layout_constraintTop_toBottomOfid/previewapp:layout_constraintLeft_toLeftOfparentapp:layout_constraintRight_toRightOfparentapp:layout_constraintHorizontal_bias0.5tools:ignoreMissingConstraints //androidx.constraintlayout.widget.ConstraintLayout
2.4 解码 demo实现效果
这里是找一个mp4格式的测试视频使用ffmpeg将mp4格式中的视频码流输出出来。使用命令为
$ffmpeg -i inputtest.mp4 -vcodec libx264 -preset slow -b:v 2000k -crf 21 out.h264
将其push到sd卡上完整路径为/sdcard/Download/ags/outputtest4.h264。实际运行效果展示如下