Android 5.0(L) 的MediaSession架构分析(转载)

本文介绍如何在Android Lollipop中利用MediaStyle通知和MediaSession实现锁屏媒体控制。通过定义权限、创建服务及交互逻辑,实现了播放状态控制等功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Using Android Media Style notifications with Media Session controls

Android Lollipop is brimming with new features, and one of them is lock screen notifications. Up until this point, lock screen media controls had to be implemented through the use of a RemoteView, and media control notifications had to be built with custom views. In this tutorial I will go over using the new MediaStyle for notifications and interacting with a MediaSession for controlling media playback states. All code for this tutorial can be found on GitHub.

enter image description here

The very first thing that we're going to need to do is set the MEDIA_CONTENT_CONTROL permission in AndroidManifest.xml. This will allow us to use our lock screen notification to control media.

  
  1. <permission android:name="android.permission.MEDIA_CONTENT_CONTROL" />

For this example we're going to use a background service to controls our media and build/interact with the new notifications. Our MainActivity is going to be very straight forward and simply start our service with an action telling the service to build a playing state notification.

  
  1. Intent intent = new Intent( getApplicationContext(), MediaPlayerService.class );
  2. intent.setAction( MediaPlayerService.ACTION_PLAY );
  3. startService( intent );

Next, we're going to want to start fleshing out MediaPlayerService. At the top of the class we're going to define a set of strings that we will use to implement notification actions, and also define the objects that we'll use throughout the class.

  
  1. public static final String ACTION_PLAY = "action_play";
  2. public static final String ACTION_PAUSE = "action_pause";
  3. public static final String ACTION_REWIND = "action_rewind";
  4. public static final String ACTION_FAST_FORWARD = "action_fast_foward";
  5. public static final String ACTION_NEXT = "action_next";
  6. public static final String ACTION_PREVIOUS = "action_previous";
  7. public static final String ACTION_STOP = "action_stop";
  8. private MediaPlayer mMediaPlayer;
  9. private MediaSessionManager mManager;
  10. private MediaSession mSession;
  11. private MediaController mController;

When the service receives an intent, it'll immediately go through onStartCommand. This method only does two simple things: if our objects have not been initialized, it'll call initMediaSession to set them up, and then the intent will be passed to handleIntent.

initMediaSession initializes the objects that we defined earlier. MediaPlayer handles media playback (not used in this example, but in an actual app it would be), the MediaSessionManagerhelps maintain the MediaSession, the MediaSession itself is used for keeping track of media states, and the MediaController handles transitioning between media states and callingMediaPlayer methods.

  
  1. mMediaPlayer = new MediaPlayer();
  2. mManager = (MediaSessionManager) getSystemService(Context.MEDIA_SESSION_SERVICE);
  3. mSession = mManager.createSession("sample session");
  4. mController = MediaController.fromToken( mSession.getSessionToken() );

The next thing initMediaSession does is add TransportControlCallbacks that we can call in order to control the MediaPlayer and display new notifications.

  
  1. mSession.addTransportControlsCallback( new MediaSession.TransportControlsCallback() {
  2.     @Override
  3.     public void onPlay() {
  4.         super.onPlay();
  5.         Log.e( "MediaPlayerService", "onPlay");
  6.         buildNotification( generateAction( android.R.drawable.ic_media_pause, "Pause", ACTION_PAUSE ) );
  7.     }
  8.     @Override
  9.     public void onPause() {
  10.         super.onPause();
  11.         Log.e( "MediaPlayerService", "onPause");
  12.         buildNotification(generateAction(android.R.drawable.ic_media_play, "Play", ACTION_PLAY));
  13.     }
  14.     @Override
  15.     public void onSkipToNext() {
  16.         super.onSkipToNext();
  17.         Log.e( "MediaPlayerService", "onSkipToNext");
  18.         //Change media here
  19.         buildNotification( generateAction( android.R.drawable.ic_media_pause, "Pause", ACTION_PAUSE ) );
  20.     }
  21.     @Override
  22.     public void onSkipToPrevious() {
  23.         super.onSkipToPrevious();
  24.         Log.e( "MediaPlayerService", "onSkipToPrevious");
  25.         //Change media here
  26.         buildNotification( generateAction( android.R.drawable.ic_media_pause, "Pause", ACTION_PAUSE ) );
  27.     }
  28.     @Override
  29.     public void onFastForward() {
  30.         super.onFastForward();
  31.         Log.e( "MediaPlayerService", "onFastForward");
  32.         //Manipulate current media here
  33.     }
  34.     @Override
  35.     public void onRewind() {
  36.         super.onRewind();
  37.         Log.e( "MediaPlayerService", "onRewind");
  38.         //Manipulate current media here
  39.     }
  40.     @Override
  41.     public void onStop() {
  42.         super.onStop();
  43.         Log.e( "MediaPlayerService", "onStop");
  44.         //Stop media player here
  45.         NotificationManager notificationManager = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
  46.         notificationManager.cancel( 1 );
  47.         Intent intent = new Intent( getApplicationContext(), MediaPlayerService.class );
  48.         stopService( intent );
  49.     }
  50.     @Override
  51.     public void onSeekTo(long pos) {
  52.         super.onSeekTo(pos);
  53.     }
  54.     @Override
  55.     public void onSetRating(Rating rating) {
  56.         super.onSetRating(rating);
  57.     }
  58. });

These control methods are called in handleIntent. When an intent is received by the service,handleIntent extracts the action associated with that intent to determine which transport control method should be called.

  
  1. private void handleIntent( Intent intent ) {
  2.     if( intent == null || intent.getAction() == null )
  3.         return;
  4.     String action = intent.getAction();
  5.     if( action.equalsIgnoreCase( ACTION_PLAY ) ) {
  6.         mController.getTransportControls().play();
  7.     } else if( action.equalsIgnoreCase( ACTION_PAUSE ) ) {
  8.         mController.getTransportControls().pause();
  9.     } else if( action.equalsIgnoreCase( ACTION_FAST_FORWARD ) ) {
  10.         mController.getTransportControls().fastForward();
  11.     } else if( action.equalsIgnoreCase( ACTION_REWIND ) ) {
  12.         mController.getTransportControls().rewind();
  13.     } else if( action.equalsIgnoreCase( ACTION_PREVIOUS ) ) {
  14.         mController.getTransportControls().skipToPrevious();
  15.     } else if( action.equalsIgnoreCase( ACTION_NEXT ) ) {
  16.         mController.getTransportControls().skipToNext();
  17.     } else if( action.equalsIgnoreCase( ACTION_STOP ) ) {
  18.         mController.getTransportControls().stop();
  19.     }
  20. }

As can be seen in the TransportControlCallbacks, we call buildNotification with another method called generateAction. Actions are used by the MediaStyle notification to populate the buttons at the bottom of the notification and launch intents when pressed. generateAction simply accepts the icon that the notification will use for that button, a title for the button and a string that will be used as the action identifier in handleIntent. With this information, generateAction is able to construct a pendingIntent before assigning it to an action that is then returned.

  
  1. private Notification.Action generateAction( int icon, String title, String intentAction ) {
  2.     Intent intent = new Intent( getApplicationContext(), MediaPlayerService.class );
  3.     intent.setAction( intentAction );
  4.     PendingIntent pendingIntent = PendingIntent.getService(getApplicationContext(), 1, intent, 0);
  5.     return new Notification.Action.Builder( icon, title, pendingIntent ).build();
  6. }

buildNotification is where we actually implement the MediaStyle notification. The first thing we do is create a new Notification.MediaStyle style, then start building the rest of the notification through using Notification.Builder. This notification simply contains apendingIntent that would stop our media when the notification is dismissed, a title, content text, a small icon and the style.

  
  1. private void buildNotification( Notification.Action action ) {
  2.     Notification.MediaStyle style = new Notification.MediaStyle();
  3.     Intent intent = new Intent( getApplicationContext(), MediaPlayerService.class );
  4.     intent.setAction( ACTION_STOP );
  5.     PendingIntent pendingIntent = PendingIntent.getService(getApplicationContext(), 1, intent, 0);
  6.     Notification.Builder builder = new Notification.Builder( this )
  7.             .setSmallIcon( R.drawable.ic_launcher )
  8.             .setContentTitle( "Media Title" )
  9.             .setContentText( "Media Artist" )
  10.             .setDeleteIntent( pendingIntent )
  11.             .setStyle( style );

Next we can add our buttons to the notification through the use of Builder.addAction. It should be noted that MediaStyle notifications only support up to five different actions. Each of our actions will be generated through the generateAction method described above.

  
  1. builder.addAction( generateAction( android.R.drawable.ic_media_previous, "Previous", ACTION_PREVIOUS ) );
  2. builder.addAction( generateAction( android.R.drawable.ic_media_rew, "Rewind", ACTION_REWIND ) );
  3. builder.addAction( action );
  4. builder.addAction( generateAction( android.R.drawable.ic_media_ff, "Fast Foward", ACTION_FAST_FORWARD ) );
  5. builder.addAction( generateAction( android.R.drawable.ic_media_next, "Next", ACTION_NEXT ) );

The last thing this method does is actually construct the notification and post it to the system

  
  1. NotificationManager notificationManager = (NotificationManager) getSystemService( Context.NOTIFICATION_SERVICE );
  2. notificationManager.notify( 1, builder.build() );

Now that the notification and sessions are implementing and working, the final thing we need to take into account is releasing our MediaSession once the media player and service have been stopped.

  
  1. @Override
  2. public boolean onUnbind(Intent intent) {
  3.     mSession.release();
  4.     return super.onUnbind(intent);
  5. }

And with that we now have a fully working MediaStyle notification on our lock screen and in the notification drawer that takes advantage of MediaSession for playback control. Enjoy!

Using Android Media Style notifications with Media Session controls

Android Lollipop is brimming with new features, and one of them is lock screen notifications. Up until this point, lock screen media controls had to be implemented through the use of a RemoteView, and media control notifications had to be built with custom views. In this tutorial I will go over using the new MediaStyle for notifications and interacting with a MediaSession for controlling media playback states. All code for this tutorial can be found on GitHub.

enter image description here

The very first thing that we're going to need to do is set the MEDIA_CONTENT_CONTROL permission in AndroidManifest.xml. This will allow us to use our lock screen notification to control media.

  
  1. <permission android:name="android.permission.MEDIA_CONTENT_CONTROL" />

For this example we're going to use a background service to controls our media and build/interact with the new notifications. Our MainActivity is going to be very straight forward and simply start our service with an action telling the service to build a playing state notification.

  
  1. Intent intent = new Intent( getApplicationContext(), MediaPlayerService.class );
  2. intent.setAction( MediaPlayerService.ACTION_PLAY );
  3. startService( intent );

Next, we're going to want to start fleshing out MediaPlayerService. At the top of the class we're going to define a set of strings that we will use to implement notification actions, and also define the objects that we'll use throughout the class.

  
  1. public static final String ACTION_PLAY = "action_play";
  2. public static final String ACTION_PAUSE = "action_pause";
  3. public static final String ACTION_REWIND = "action_rewind";
  4. public static final String ACTION_FAST_FORWARD = "action_fast_foward";
  5. public static final String ACTION_NEXT = "action_next";
  6. public static final String ACTION_PREVIOUS = "action_previous";
  7. public static final String ACTION_STOP = "action_stop";
  8. private MediaPlayer mMediaPlayer;
  9. private MediaSessionManager mManager;
  10. private MediaSession mSession;
  11. private MediaController mController;

When the service receives an intent, it'll immediately go through onStartCommand. This method only does two simple things: if our objects have not been initialized, it'll call initMediaSession to set them up, and then the intent will be passed to handleIntent.

initMediaSession initializes the objects that we defined earlier. MediaPlayer handles media playback (not used in this example, but in an actual app it would be), the MediaSessionManagerhelps maintain the MediaSession, the MediaSession itself is used for keeping track of media states, and the MediaController handles transitioning between media states and callingMediaPlayer methods.

  
  1. mMediaPlayer = new MediaPlayer();
  2. mManager = (MediaSessionManager) getSystemService(Context.MEDIA_SESSION_SERVICE);
  3. mSession = mManager.createSession("sample session");
  4. mController = MediaController.fromToken( mSession.getSessionToken() );

The next thing initMediaSession does is add TransportControlCallbacks that we can call in order to control the MediaPlayer and display new notifications.

  
  1. mSession.addTransportControlsCallback( new MediaSession.TransportControlsCallback() {
  2.     @Override
  3.     public void onPlay() {
  4.         super.onPlay();
  5.         Log.e( "MediaPlayerService", "onPlay");
  6.         buildNotification( generateAction( android.R.drawable.ic_media_pause, "Pause", ACTION_PAUSE ) );
  7.     }
  8.     @Override
  9.     public void onPause() {
  10.         super.onPause();
  11.         Log.e( "MediaPlayerService", "onPause");
  12.         buildNotification(generateAction(android.R.drawable.ic_media_play, "Play", ACTION_PLAY));
  13.     }
  14.     @Override
  15.     public void onSkipToNext() {
  16.         super.onSkipToNext();
  17.         Log.e( "MediaPlayerService", "onSkipToNext");
  18.         //Change media here
  19.         buildNotification( generateAction( android.R.drawable.ic_media_pause, "Pause", ACTION_PAUSE ) );
  20.     }
  21.     @Override
  22.     public void onSkipToPrevious() {
  23.         super.onSkipToPrevious();
  24.         Log.e( "MediaPlayerService", "onSkipToPrevious");
  25.         //Change media here
  26.         buildNotification( generateAction( android.R.drawable.ic_media_pause, "Pause", ACTION_PAUSE ) );
  27.     }
  28.     @Override
  29.     public void onFastForward() {
  30.         super.onFastForward();
  31.         Log.e( "MediaPlayerService", "onFastForward");
  32.         //Manipulate current media here
  33.     }
  34.     @Override
  35.     public void onRewind() {
  36.         super.onRewind();
  37.         Log.e( "MediaPlayerService", "onRewind");
  38.         //Manipulate current media here
  39.     }
  40.     @Override
  41.     public void onStop() {
  42.         super.onStop();
  43.         Log.e( "MediaPlayerService", "onStop");
  44.         //Stop media player here
  45.         NotificationManager notificationManager = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
  46.         notificationManager.cancel( 1 );
  47.         Intent intent = new Intent( getApplicationContext(), MediaPlayerService.class );
  48.         stopService( intent );
  49.     }
  50.     @Override
  51.     public void onSeekTo(long pos) {
  52.         super.onSeekTo(pos);
  53.     }
  54.     @Override
  55.     public void onSetRating(Rating rating) {
  56.         super.onSetRating(rating);
  57.     }
  58. });

These control methods are called in handleIntent. When an intent is received by the service,handleIntent extracts the action associated with that intent to determine which transport control method should be called.

  
  1. private void handleIntent( Intent intent ) {
  2.     if( intent == null || intent.getAction() == null )
  3.         return;
  4.     String action = intent.getAction();
  5.     if( action.equalsIgnoreCase( ACTION_PLAY ) ) {
  6.         mController.getTransportControls().play();
  7.     } else if( action.equalsIgnoreCase( ACTION_PAUSE ) ) {
  8.         mController.getTransportControls().pause();
  9.     } else if( action.equalsIgnoreCase( ACTION_FAST_FORWARD ) ) {
  10.         mController.getTransportControls().fastForward();
  11.     } else if( action.equalsIgnoreCase( ACTION_REWIND ) ) {
  12.         mController.getTransportControls().rewind();
  13.     } else if( action.equalsIgnoreCase( ACTION_PREVIOUS ) ) {
  14.         mController.getTransportControls().skipToPrevious();
  15.     } else if( action.equalsIgnoreCase( ACTION_NEXT ) ) {
  16.         mController.getTransportControls().skipToNext();
  17.     } else if( action.equalsIgnoreCase( ACTION_STOP ) ) {
  18.         mController.getTransportControls().stop();
  19.     }
  20. }

As can be seen in the TransportControlCallbacks, we call buildNotification with another method called generateAction. Actions are used by the MediaStyle notification to populate the buttons at the bottom of the notification and launch intents when pressed. generateAction simply accepts the icon that the notification will use for that button, a title for the button and a string that will be used as the action identifier in handleIntent. With this information, generateAction is able to construct a pendingIntent before assigning it to an action that is then returned.

  
  1. private Notification.Action generateAction( int icon, String title, String intentAction ) {
  2.     Intent intent = new Intent( getApplicationContext(), MediaPlayerService.class );
  3.     intent.setAction( intentAction );
  4.     PendingIntent pendingIntent = PendingIntent.getService(getApplicationContext(), 1, intent, 0);
  5.     return new Notification.Action.Builder( icon, title, pendingIntent ).build();
  6. }

buildNotification is where we actually implement the MediaStyle notification. The first thing we do is create a new Notification.MediaStyle style, then start building the rest of the notification through using Notification.Builder. This notification simply contains apendingIntent that would stop our media when the notification is dismissed, a title, content text, a small icon and the style.

  
  1. private void buildNotification( Notification.Action action ) {
  2.     Notification.MediaStyle style = new Notification.MediaStyle();
  3.     Intent intent = new Intent( getApplicationContext(), MediaPlayerService.class );
  4.     intent.setAction( ACTION_STOP );
  5.     PendingIntent pendingIntent = PendingIntent.getService(getApplicationContext(), 1, intent, 0);
  6.     Notification.Builder builder = new Notification.Builder( this )
  7.             .setSmallIcon( R.drawable.ic_launcher )
  8.             .setContentTitle( "Media Title" )
  9.             .setContentText( "Media Artist" )
  10.             .setDeleteIntent( pendingIntent )
  11.             .setStyle( style );

Next we can add our buttons to the notification through the use of Builder.addAction. It should be noted that MediaStyle notifications only support up to five different actions. Each of our actions will be generated through the generateAction method described above.

  
  1. builder.addAction( generateAction( android.R.drawable.ic_media_previous, "Previous", ACTION_PREVIOUS ) );
  2. builder.addAction( generateAction( android.R.drawable.ic_media_rew, "Rewind", ACTION_REWIND ) );
  3. builder.addAction( action );
  4. builder.addAction( generateAction( android.R.drawable.ic_media_ff, "Fast Foward", ACTION_FAST_FORWARD ) );
  5. builder.addAction( generateAction( android.R.drawable.ic_media_next, "Next", ACTION_NEXT ) );

The last thing this method does is actually construct the notification and post it to the system

  
  1. NotificationManager notificationManager = (NotificationManager) getSystemService( Context.NOTIFICATION_SERVICE );
  2. notificationManager.notify( 1, builder.build() );

Now that the notification and sessions are implementing and working, the final thing we need to take into account is releasing our MediaSession once the media player and service have been stopped.

  
  1. @Override
  2. public boolean onUnbind(Intent intent) {
  3.     mSession.release();
  4.     return super.onUnbind(intent);
  5. }

And with that we now have a fully working MediaStyle notification on our lock screen and in the notification drawer that takes advantage of MediaSession for playback control. Enjoy!

<think>首先,用户的问题是关于Android系统中Camera1和Camera2如何将数据传递给MediaCodec进行编码的具体流程。用户想知道在哪个步骤数据被传递给MediaCodec。 我需要参考提供的引用内容来构建回答。引用包括: - [^1]: 讨论了Camera架构、Camera2优点、使用实例、API1和API2的比较等。 - [^2]: 关于MediaCodec视频编码的系列文章,特别是编码YUV数据。 - [^3]: 具体代码示例,展示如何使用MediaCodec初始化视频编码器,处理YUV数据。 - [^4]: 讨论了如何将相机预览传递给MediaCodec.createInputSurface()创建的Surface,提到了共享EGL上下文。 用户的问题焦点是数据传递的具体步骤,所以我需要基于这些引用,解释Camera1和Camera2的不同实现方式。 关键点: - Camera1:通常使用SurfaceView、TextureView或GLSurfaceView来显示预览,同时可以将数据传递给MediaCodec。 - Camera2:使用Camera2 API,可以更直接地将数据传递给MediaCodec,特别是通过createInputSurface()方法。 具体步骤: 1. **Camera1:** - 在Camera1中,数据传递通常通过设置预览回调或使用Surface。 - 引用[^2]提到Android Camera系列文章,包括使用SurfaceView、TextureView、GLSurfaceView等。 - 引用[^3]显示,Camera1可以通过回调获取YUV数据,然后传递给MediaCodec。 - 步骤:打开Camera -> 设置预览Surface -> 启动预览。数据可以通过onPreviewFrame回调获取YUV数据,然后手动输入到MediaCodec。 - 或者,直接将Surface提供给Camera,这样数据直接流到MediaCodec的输入Surface。 2. **Camera2:** - Camera2 API更现代,支持直接输出到Surface。 - 引用[^4]讨论如何将相机预览传递给MediaCodec.createInputSurface()创建的Surface。 - 在Camera2中,使用createCaptureSession时,可以添加MediaCodec的InputSurface作为输出Surface。 - 步骤:创建MediaCodec -> 获取InputSurface -> 在CameraDevice.createCaptureSession中添加这个Surface -> 启动预览,数据直接流到MediaCodec。 引用支持: - 引用[^3]:展示了如何初始化MediaCodec,并设置颜色格式为YUV420Flexible,这适用于Camera数据。 - 引用[^4]:解释了如何共享EGL上下文,以便Camera预览数据可以直接渲染到MediaCodec的Surface。 现在,构建回答结构: 1. **引言**:简要介绍Camera1和Camera2在数据传递上的差异。 2. **Camera1数据传递流程**: - 使用预览回调手动获取数据。 - 或直接设置Surface到MediaCodec。 3. **Camera2数据传递流程**: - 使用createInputSurface()。 - 在CaptureSession中添加Surface。 4. **关键区别**:Camera2更高效,支持零拷贝。 5. **生成相关问题**:作为系统指令的一部分。 回答使用中文,确保清晰、结构良好。 最后,添加引用标识在相关段落末尾。 生成相关问题: 基于主题,例如: 1. Camera1和Camera2在性能上有什么差异? 2. 如何优化Camera到MediaCodec的数据传递? 3. MediaCodec编码YUV数据的常见问题是什么?</think>### Android Camera1 和 Camera2 数据传递到 MediaCodec 的流程 在 Android 系统中,Camera1 (API1) 和 Camera2 (API2) 将数据传递给 MediaCodec 进行编码的具体流程有所不同。核心区别在于:Camera1 通常通过回调或手动设置 Surface 实现数据传递,而 Camera2 则利用 `createInputSurface()` 方法实现更高效的直接传递。下面我将逐步解释两者的具体流程,基于引用内容进行说明。 #### 1. **Camera1 (API1) 数据传递流程** Camera1 使用较旧的 API,数据传递主要通过以下两种方式实现: - **方式一:通过预览回调手动获取数据** - 步骤: 1. 打开 Camera:调用 `Camera.open()` 获取 Camera 实例。 2. 设置预览回调:注册 `Camera.PreviewCallback`,在 `onPreviewFrame()` 中获取原始的 YUV 数据(如 NV21 格式)。 3. 手动输入到 MediaCodec:将回调中的 YUV 数据转换为 MediaCodec 支持的格式(如 I420 或 NV12),然后通过 `MediaCodec.queueInputBuffer()` 输入到编码器。 - 关键点:这种方式涉及数据拷贝,可能影响性能。适用于需要自定义处理数据的场景。 - 代码示例参考: ```java camera.setPreviewCallback(new Camera.PreviewCallback() { @Override public void onPreviewFrame(byte[] data, Camera camera) { // 将 data (YUV) 转换为 MediaCodec 所需格式 byte[] processedData = convertToYUV420(data); // 输入到 MediaCodec int inputBufferIndex = mediaCodec.dequeueInputBuffer(TIMEOUT_US); if (inputBufferIndex >= 0) { ByteBuffer inputBuffer = mediaCodec.getInputBuffer(inputBufferIndex); inputBuffer.put(processedData); mediaCodec.queueInputBuffer(inputBufferIndex, 0, processedData.length, presentationTimeUs, 0); } } }); ``` 引用[^3] 中提到,MediaCodec 初始化时需设置颜色格式(如 `COLOR_FormatYUV420Flexible`),以兼容 YUV 数据输入。 - **方式二:直接设置 Surface 到 Camera** - 步骤: 1. 创建 MediaCodec 并获取输入 Surface:通过 `MediaCodec.createInputSurface()` 获取一个 Surface。 2. 将 Surface 绑定到 Camera:调用 `Camera.setPreviewTexture()` 或 `Camera.setPreviewDisplay()`,传入这个 Surface。 3. 启动预览:数据直接流到 MediaCodec,无需手动回调。 - 关键点:这种方式减少了数据拷贝,但依赖于 EGL 上下文共享。如果 Surface 由 MediaCodec 创建,Camera 会将预览帧直接渲染到该 Surface。 - 引用[^4] 指出,Camera1 可能需要处理 EGL 上下文共享问题,以确保预览数据能正确传递到 MediaCodec 的 Surface。 总结 Camera1 流程:数据传递发生在预览启动后,通过回调或 Surface 绑定实现。性能较低,适合简单应用[^1][^3]。 #### 2. **Camera2 (API2) 数据传递流程** Camera2 API 更现代,支持零拷贝(zero-copy)数据传递,主要通过 `createInputSurface()` 实现: - **步骤详解**: 1. **初始化 MediaCodec**:创建 MediaCodec 编码器,并配置格式(如 H.264)。例如: ```java MediaFormat format = MediaFormat.createVideoFormat("video/avc", width, height); format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible); format.setInteger(MediaFormat.KEY_BIT_RATE, 1500000); MediaCodec mediaCodec = MediaCodec.createEncoderByType("video/avc"); mediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); ``` 引用[^3] 强调了颜色格式的配置,以确保兼容 Camera 输出的 YUV 数据。 2. **获取 MediaCodec 的 InputSurface**:调用 `mediaCodec.createInputSurface()` 创建一个 Surface。这个 Surface 将作为 Camera2 的输出目标。 3. **配置 CameraCaptureSession**: - 打开 CameraDevice 后,创建 CaptureRequest.Builder 用于预览。 - 在 `CameraDevice.createCaptureSession()` 中,将 MediaCodec 的 InputSurface 添加到输出 Surface 列表中。 - 启动预览:数据直接从 Camera 硬件流到 MediaCodec,无需应用层干预。 ```java Surface inputSurface = mediaCodec.createInputSurface(); List<Surface> outputSurfaces = new ArrayList<>(); outputSurfaces.add(inputSurface); // 添加 MediaCodec Surface outputSurfaces.add(previewSurface); // 可选:添加预览 Surface cameraDevice.createCaptureSession(outputSurfaces, new CameraCaptureSession.StateCallback() { @Override public void onConfigured(CameraCaptureSession session) { CaptureRequest.Builder requestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); requestBuilder.addTarget(inputSurface); // 目标 Surface 设置为 MediaCodec session.setRepeatingRequest(requestBuilder.build(), null, null); } }, null); ``` 4. **MediaCodec 处理数据**:启动 MediaCodec 后,数据自动从 Camera 流入编码器。编码后的数据通过 `MediaCodec.getOutputBuffer()` 获取。 - **关键点**: - 零拷贝优势:Camera2 直接在硬件层传递数据,减少了 CPU 开销和延迟。 - EGL 上下文处理:引用[^4] 提到,Camera2 可能需要共享 EGL 上下文(如在 OpenGL ES 渲染中),以确保 Surface 兼容性。例如,在 GLSurfaceView 中,通过离屏渲染实现共享。 - 适用场景:高效视频录制或直播,如引用[^2] 中描述的 MediaCodec 编码 YUV 数据流程。 总结 Camera2 流程:数据传递发生在 `createCaptureSession()` 步骤,通过将 MediaCodec Surface 添加为输出目标实现。性能更高,是推荐方式[^1][^4]。 #### 3. **关键区别与性能对比** - **传递步骤位置**: - Camera1:数据传递在预览回调或 Surface 绑定步骤。 - Camera2:数据传递在 CaptureSession 配置步骤。 - **性能**:Camera2 支持零拷贝,延迟更低;Camera1 涉及数据拷贝,可能成为瓶颈。 - **兼容性**:Camera1 兼容旧设备;Camera2 需要 Android 5.0+,并提供更精细控制。 - 引用[^1] 比较了 API1 和 API2,指出 Camera2 在功能设置和效率上更优。 #### 注意事项 - **数据格式**:Camera 输出通常是 YUV(如 NV21),MediaCodec 需配置为兼容格式(如 YUV420Flexible)。引用[^3] 详细讨论了 NV21、I420 等格式的转换。 - **错误处理**:确保 MediaCodec 和 Camera 的生命周期同步,避免资源泄漏。 - 实际开发中,建议优先使用 Camera2 以利用硬件加速。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值