Android媒体应用(四)--构建音频应用程序

Android基于MediaBroswerService的App实现概述,最新Android面试合集 可以将音乐播放器的状态同步到第三方App 能够和第三方App间进行相互通信 类似于CarLife 对音乐App的唤起,首先第三方App开启后,即可拉起音乐App,然后获取其中的歌单,打开歌单之后,获取歌单内的歌曲列表,点击进行播放,可以进行播放,暂停,下一首,上一首的控制。 技术实现 谷歌官方提供了MediaBroswerService,通过其可以帮助我们实现上述的需求。 MediaBroswerService Android多媒体架构 Android多媒体播放采用client,server架构,一. 阅读详情

原文地址:https://developer.android.google.cn/guide/topics/media-apps/audio-app/building-an-audio-app.html

音频应用程序的首选架构是客户端/服务器设计。播放器及其media session 在MediaBrowserService中实现,UI、media controller 与MediaBrowser都在Android Activity中。

MediaBrowserService提供两个主要功能:

  1. 当您使用MediaBrowserService时,MediaBrowser的其他组件和应用程序可以发现您的服务,创建自己的media controller,连接到media session并控制播放器。这正是Android Wear和Android Auto Applications获取到您的媒体应用程序的权限的原因。
  2. 它还提供可选的浏览API。应用程序不必使用此功能。浏览API允许客户端查询服务并构建其内容层次结构的表示,其可以表示播放列表,媒体库或其他类型的集合。

注意:像media session和media controller一样, media browser services and media browsers 的推荐实现是MediaBrowserServiceCompat和MediaBrowserCompat,它们在media-compat支持库中定义。它们替换了在API 21中引入的MediaBrowserService和MediaBrowser类的早期版本。为简洁起见,术语“MediaBrowserService”和“MediaBrowser”分别指MediaBrowserServiceCompat和MediaBrowserCompat的实例。

1.构建Media Browser Service

如何创建包含media session的media browser service,管理客户端连接,并在播放音频时成为前台服务。

您的应用程序必须在其清单中声明具有意图过滤器的MediaBrowserService。 您可以选择自己的服务名称; 在下面的例子中,它是“MediaPlaybackService”。

<service android:name=".MediaPlaybackService">
  <intent-filter>
    <action android:name="android.media.browse.MediaBrowserService" />
  </intent-filter>
</service>
注意:MediaBrowserService的推荐实现是MediaBrowserServiceCompat。 这在media-compat支持库中定义。 在此页面中,“MediaBrowserService”是指MediaBrowserServiceCompat的一个实例.

初始化media session

当服务收到onCreate()生命周期回调方法时,它应该执行以下步骤:

  1. 创建并初始化media session
  2. 设置media session回调
  3. 设置media session token
下面的onCreate()代码演示了以下步骤:

public class MediaPlaybackService extends MediaBrowserServiceCompat {
    private static final String MY_MEDIA_ROOT_ID = "media_root_id";
    private static final String MY_EMPTY_MEDIA_ROOT_ID = "empty_root_id";

    private MediaSessionCompat mMediaSession;
    private PlaybackStateCompat.Builder mStateBuilder;

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

        // Create a MediaSessionCompat
        mMediaSession = new MediaSessionCompat(context, LOG_TAG);

        // Enable callbacks from MediaButtons and TransportControls
        mMediaSession.setFlags(
              MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS |
              MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);

        // Set an initial PlaybackState with ACTION_PLAY, so media buttons can start the player
        mStateBuilder = new PlaybackStateCompat.Builder()
                            .setActions(
                                PlaybackStateCompat.ACTION_PLAY |
                                PlaybackStateCompat.ACTION_PLAY_PAUSE);
        mMediaSession.setPlaybackState(mStateBuilder.build());

        // MySessionCallback() has methods that handle callbacks from a media controller
        mMediaSession.setCallback(new MySessionCallback());

        // Set the session's token so that client activities can communicate with it.
        setSessionToken(mMediaSession.getSessionToken());
    }
}

管理客户端连接

MediaBrowserService有两种处理客户端连接的方法:onGetRoot()控制对服务的访问,而onLoadChildren()提供了客户端构建和显示MediaBrowserService内容层次结构菜单的功能。

使用onGetRoot()控制客户端连接

onGetRoot()方法返回内容层次结构的根节点。 如果方法返回null,则拒绝连接。


要允许客户端连接到您的服务并浏览其媒体内容,onGetRoot()必须返回一个非空的BrowserRoot,它是一个代表您的内容层次结构的根ID。


要允许客户端连接到MediaSession而不浏览,onGetRoot()仍然必须返回一个非空的BrowserRoot,但是根ID应该表示一个空的内容层次结构。


onGetRoot()的典型实现可能如下所示:

@Override
public BrowserRoot onGetRoot(String clientPackageName, int clientUid,
    Bundle rootHints) {

    // (Optional) Control the level of access for the specified package name.
    // You'll need to write your own logic to do this.
    if (allowBrowsing(clientPackageName, clientUid)) {
        // Returns a root ID that clients can use with onLoadChildren() to retrieve
        // the content hierarchy.
        return new BrowserRoot(MY_MEDIA_ROOT_ID, null);
    } else {
        // Clients can connect, but this BrowserRoot is an empty hierachy
        // so onLoadChildren returns nothing. This disables the ability to browse for content.
        return new BrowserRoot(MY_EMPTY_MEDIA_ROOT_ID, null);
    }
}

在某些情况下,您可能需要实施一个白名单/黑名单方案来控制连接。有关白名单的示例,请参阅通用 Universal Android Music Player 的 PackageValidator类。

注意:您应该考虑根据客户端进行查询的类型提供不同的内容层次结构。特别是,Android Auto限制用户如何与音频应用程序交互。有关详细信息,请参阅为 自动播放音频。您可以在连接时查看clientPackageName以确定客户端类型,并根据客户端(或rootHints(如果有))返回不同的BrowserRoot。

与onLoadChildren()通信内容

客户端连接后,可以通过重复调用MediaBrowserCompat.subscribe()来构建UI的本地表示,从而遍历内容层次。 subscribe()方法将回调的onLoadChildren()发送到service,该service返回MediaBrowser.MediaItem对象的列表。

每个MediaItem都有唯一的ID字符串,它是不透明的令牌。当客户端打开子菜单或播放项目时,会传递ID。service负责将ID与相应的菜单节点或内容项相关联。

onLoadChildren()的简单实现可能如下所示:

@Override
public void onLoadChildren(final String parentMediaId,
    final Result<List<MediaItem>> result) {

    //  Browsing not allowed
    if (TextUtils.equals(EMPTY_MEDIA_ROOT_ID, parentMediaId)) {
        result.sendResult(null);
        return;
    }

    // Assume for example that the music catalog is already loaded/cached.

    List<MediaItem> mediaItems = new ArrayList<>();

    // Check if this is the root menu:
    if (MY_MEDIA_ROOT_ID.equals(parentMediaId)) {
        // Build the MediaItem objects for the top level,
        // and put them in the mediaItems list...
    } else {
        // Examine the passed parentMediaId to see which submenu we're at,
        // and put the children of that menu in the mediaItems list...
    }
    result.sendResult(mediaItems);
}

注意:MediaBrowserService传递的MediaItem对象不应包含图标位图。当为每个项目构建MediaDescription时,使用Uri代替调用setIconUri()。

有关如何实现onLoadChildren()的示例,请参阅MediaBrowserService和Universal Android Music Player示例应用程序。

media browser service 生命周期

Android service的行为取决于是启动还是绑定到一个或多个客户端。创建service后,可以启动,绑定或同时用两种。在所有这些状态中,它都是功能齐全的,可以执行其设计的工作。不同的是service的将存在多长时间。绑定的service在所有绑定的客户端解除绑定之前不会被销毁。启动的service可以被显式地停止和销毁(假设它不再绑定到任何客户端)。

当在另一个Activity中运行的MediaBrowser连接到MediaBrowserService时,它将活动绑定到服务,使服务受限(但未启动)。此默认行为内置于MediaBrowserServiceCompat类中。

当service所有客户端解除绑定时,只有绑定(而不是启动)的servcie会被销毁。如果您的UI Activity在此时断开连接,则该service将被销毁。如果您还没有播放任何音乐,这不是问题。但是,播放开始时,即使切换应用程序,用户也可能希望继续聆听。当使用其他应用时,并不能销毁播放器。

因此,您需要确保通过调用startService()启动service。必须明确停止启动的服务,无论是否绑定。这样可以确保即使控制UI活动解除绑定,您的播放器也会继续执行。


要停止启动的服务,请调用Context.stopService()或stopSelf()。系统尽快停止并破坏服务。但是,如果一个或多个客户端仍然绑定到服务,则停止服务的呼叫将被延迟,直到其所有客户端解除绑定。

MediaBrowserService的生命周期由创建的方式控制,客户端的数量以及从媒体会话回调接收的呼叫。总结:

  1. service是在响应媒体按钮启动时创建的,或者当活动绑定到媒体按钮(通过其MediaBrowser连接后)时创建。
  2. media session 的onPlay()回调应该包括调用startService()的代码。即使在绑定到它的所有UI MediaBrowser活动解除绑定的情况下,也可以确保服务启动并继续运行。
  3. onStop()回调应该调用stopSelf()。如果服务已启动,则会停止该服务。此外,如果没有Activity绑定service,service将被销毁。否则,service将保持绑定,直到其所有Activity解除绑定。 (如果在service销毁之前接收到后续的startService()调用,则取消挂起的停止。)
以下流程图演示了如何管理服务的生命周期。可变计数器跟踪绑定的客户端数量:



使用具有前台service的MediaStyle通知
当一个服务正在播放时,它应该在前台运行。这使系统知道服务正在执行有用的功能,如果系统内存不足,则不应该被杀死。前台服务必须显示通知,以便用户知道该通知,并且可以选择性地控制它。 onPlay()回调应该把服务放在前台。 (请注意,这里“前台”的含义有些特殊。虽然Android将Service置为前台视为进程管理的目的,但是对于用户而言,播放器正在后台播放,而其他应用程序在前台。)

当service在前台运行时,它必须显示一个通知,理想情况下是使用一个或多个传输控件。该通知还应包括会话元数据中的有用信息。

播放器开始播放时构建并显示通知。最好的方法是在MediaSessionCompat.Callback.onPlay()方法中。

以下示例使用NotificationCompat.MediaStyle,它专为媒体应用程序而设计。它显示了如何构建显示元数据和传输控件的通知。 getController()方便方法可以直接从media session创建 media controller 。

// Given a media session and its context (usually the component containing the session)
// Create a NotificationCompat.Builder

// Get the session's metadata
MediaControllerCompat controller = mediaSession.getController();
MediaMetadataCompat mediaMetadata = controller.getMetadata();
MediaDescriptionCompat description = mediaMetadata.getDescription();

NotificationCompat.Builder builder = new NotificationCompat.Builder(context);

builder
    // Add the metadata for the currently playing track
    .setContentTitle(description.getTitle())
    .setContentText(description.getSubtitle())
    .setSubText(description.getDescription())
    .setLargeIcon(description.getIconBitmap())

    // Enable launching the player by clicking the notification
    .setContentIntent(controller.getSessionActivity())

    // Stop the service when the notification is swiped away
    .setDeleteIntent(MediaButtonReceiver.buildMediaButtonPendingIntent(this,
       PlaybackStateCompat.ACTION_STOP))

    // Make the transport controls visible on the lockscreen
    .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)

    // Add an app icon and set its accent color
    // Be careful about the color
    .setSmallIcon(R.drawable.notification_icon)
    .setColor(ContextCompat.getColor(this, R.color.primaryDark))

    // Add a pause button
    .addAction(new NotificationCompat.Action(
        R.drawable.pause, getString(R.string.pause),
        MediaButtonReceiver.buildMediaButtonPendingIntent(this,
            PlaybackStateCompat.ACTION_PLAY_PAUSE)))

    // Take advantage of MediaStyle features
    .setStyle(new MediaStyle()
        .setMediaSession(mediaSession.getSessionToken())
        .setShowActionsInCompactView(0)

        // Add a cancel button
       .setShowCancelButton(true)
       .setCancelButtonIntent(MediaButtonReceiver.buildMediaButtonPendingIntent(this,
           PlaybackStateCompat.ACTION_STOP)));

// Display the notification and place the service in the foreground
startForeground(id, builder.build());

使用MediaStyle通知时,请注意这些NotificationCompat设置的行为:
  1. 当您使用setContentIntent()时,您的服务将在点击通知时自动启动,方便的功能。
  2. 在类似锁屏的“不受信任”情况下,通知内容的默认可见性为VISIBILITY_PRIVATE。你可能想看到锁屏上的运输控制,所以要使用VISIBILITY_PUBLIC。
  3. 设置背景颜色时要小心。在Android 5.0或更高版本的普通通知中,颜色仅适用于小应用程序图标的背景。但对于Android 7.0之前的MediaStyle通知,该颜色用于整个通知背景。测试你的背景颜色。在眼睛上温柔,避免非常明亮或荧光的颜色。
这些设置仅在您使用NotificationCompat.MediaStyle时可用:
  1. 使用setMediaSession()将通知与您的会话相关联。这允许第三方应用和随播设备访问和控制会话。
  2. 使用setShowActionsInCompactView()添加最多3个操作以显示在通知的标准大小的contentView中。 (这里指定了暂停按钮。)
  3. 在Android 5.0(API级别21)及更高版本中,一旦服务不再在前台运行,您可以滑动通知来停止播放器。您不能在早期版本中执行此操作。要允许用户在Android 5.0(API级别21)之前删除通知并停止播放,您可以通过调用setShowCancelButton(true)和setCancelButtonIntent()在通知的右上角添加取消按钮。
当您添加暂停和取消按钮时,您需要一个PendingIntent才能附加到播放动作。 MediaButtonReceiver.buildMediaButtonPendingIntent()方法将PlaybackState操作转换为PendingIntent。

2.构建Media Browser客户端

如何创建包含UI和media controller的media browser客户端活动,并与media browser service进行连接和通信。

要完成客户端/服务器设计,您必须构建一个包含您的UI代码的活动组件,一个关联的MediaController和一个MediaBrowser。


MediaBrowser执行两个重要功能:它连接到MediaBrowserService,连接后,将为您的UI创建MediaController。


注意:MediaBrowser的推荐实现是MediaBrowserCompat,它是在Media-Compat支持库中定义的。在这个页面中,术语“MediaBrowser”是指MediaBrowserCompat的一个实例。


连接到MediaBrowserService
创建客户端活动后,它将连接到MediaBrowserService。有一个握手和舞蹈涉及。修改活动的lifecyle回调如下:


onCreate()构造一个MediaBrowserCompat。以您定义的MediaBrowserService和MediaBrowserCompat.ConnectionCallback的名称传递。
onStart()连接到MediaBrowserService。 MediaBrowserCompat.ConnectionCallback的魔法来源于此。如果连接成功,onConnect()回调将创建媒体控制器,将其链接到媒体会话,将UI控件链接到MediaController,并注册控制器以接收来自媒体会议。
当您的活动停止时,onStop()将断开MediaBrowser并取消注册MediaController.Callback。

public class MediaPlayerActivity extends AppCompatActivity {
  private MediaBrowserCompat mMediaBrowser;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // ...
    // Create MediaBrowserServiceCompat
    mMediaBrowser = new MediaBrowserCompat(this,
      new ComponentName(this, MediaPlaybackService.class),
        mConnectionCallbacks,
        null); // optional Bundle
  }

  @Override
  public void onStart() {
    super.onStart();
    mMediaBrowser.connect();
  }

  @Override
  public void onStop() {
    super.onStop();
    // (see "stay in sync with the MediaSession")
    if (MediaControllerCompat.getMediaController(MediaPlayerActivity.this) != null) {
      MediaControllerCompat.getMediaController(MediaPlayerActivity.this).unregisterCallback(controllerCallback);
    }
    mMediaBrowser.disconnect();

  }
}
自定义MediaBrowserCompat.ConnectionCallback
当您的活动构建MediaBrowserCompat时,必须创建一个ConnectionCallback的实例。 修改其onConnected()方法以从MediaBrowserService检索媒体会话令牌,并使用令牌创建MediaControllerCompat。


使用MediaControllerCompat.setMediaController()方便的方法来保存控制器的链接。 这样可以处理媒体按钮。 它还允许您在构建传输控件时调用MediaControllerCompat.getMediaController()来检索控制器。


以下代码示例显示如何修改onConnected()方法。

private final MediaBrowserCompat.ConnectionCallback mConnectionCallbacks =
  new MediaBrowserCompat.ConnectionCallback() {
    @Override
    public void onConnected() {

      // Get the token for the MediaSession
      MediaSessionCompat.Token token = mMediaBrowser.getSessionToken();

      // Create a MediaControllerCompat
      MediaControllerCompat mediaController =
        new MediaControllerCompat(MediaPlayerActivity.this, // Context
        token);

      // Save the controller
      MediaControllerCompat.setMediaController(MediaPlayerActivity.this, mediaController);

      // Finish building the UI
      buildTransportControls();
    }

    @Override
    public void onConnectionSuspended() {
      // The Service has crashed. Disable transport controls until it automatically reconnects
    }

    @Override
    public void onConnectionFailed() {
      // The Service has refused our connection
    }
  };

将您的UI连接到媒体控制器
在上面的ConnectionCallback示例代码中,包含一个调用buildTransportControls()来显示你的UI。 您需要为控制播放器的UI元素设置onClickListeners。 为每个选择适当的MediaControllerCompat.TransportControls方法。


你的代码看起来像这样,每个按钮都有一个onClickListener:

void buildTransportControls()
{
  // Grab the view for the play/pause button
  mPlayPause = (ImageView) findViewById(R.id.play_pause);

  // Attach a listener to the button
  mPlayPause.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
      // Since this is a play/pause button, you'll need to test the current state
      // and choose the action accordingly

      int pbState = MediaControllerCompat.getMediaController(MediaPlayerActivity.this).getPlaybackState().getState();
      if (pbState == PlaybackStateCompat.STATE_PLAYING) {
        MediaControllerCompat.getMediaController(MediaPlayerActivity.this).getTransportControls().pause();
      } else {
        MediaControllerCompat.getMediaController(MediaPlayerActivity.this).getTransportControls().play();
      }
  });

  MediaControllerCompat mediaController = MediaControllerCompat.getMediaController(MediaPlayerActivity.this);

  // Display the initial state
  MediaMetadataCompat metadata = mediaController.getMetadata();
  PlaybackStateCompat pbState = mediaController.getPlaybackState();

  // Register a Callback to stay in sync
  mediaController.registerCallback(controllerCallback);
}
TransportControls方法将回调发送到您的服务的媒体会话。 确保您已经为每个控件定义了相应的MediaSessionCompat.Callback方法。

与媒体会话保持同步
UI应显示媒体会话的当前状态,如其PlaybackState和Metadata所描述的。 创建传输控件时,您可以获取会话的当前状态,将其显示在UI中,并根据状态及其可用操作启用和禁用传输控件。

要在每次其状态或元数据更改时从媒体会话接收回调,请使用以下两种方法定义MediaControllerCompat.Callback:

MediaControllerCompat.Callback controllerCallback =
  new MediaControllerCompat.Callback() {
    @Override
    public void onMetadataChanged(MediaMetadataCompat metadata) {}

    @Override
    public void onPlaybackStateChanged(PlaybackStateCompat state) {}
  };

在构建传输控件时注册回调(请参阅buildTransportControls()方法),并在活动停止时取消注册(在活动的onStop()生命周期方法中)。

3.Media Session回调

介绍 media session回调方法如何管理 media session, media browser service以及其他应用程序组件,如通知和广播接收器。

您的媒体会话回调在多个API中调用方法来控制播放器,管理音频焦点,以及与媒体会话和媒体浏览器服务进行通信。

onPlay() onPause() onStop()
Audio Focus requestFocus() passing in your OnAudioFocusChangeListener.
Always call requestFocus() first, proceed only if focus is granted.
  abandonAudioFocus()
Service startService()   stopSelf()
Media Session setActive(true) 
- Update metadata and state
- Update metadata and state setActive(false) 
- Update metadata and state
Player Implementation Start the player Pause the player Stop the player
Becoming Noisy Register your BroadcastReceiver Unregister your BroadcastReceiver  
Notifications startForeground(notification) stopForeground(false) stopForeground(false)
onPlay() onPause() onStop()
Audio Focus requestFocus() passing in your OnAudioFocusChangeListener.
Always call requestFocus() first, proceed only if focus is granted.
  abandonAudioFocus()
Service startService()   stopSelf()
Media Session setActive(true) 
- Update metadata and state
- Update metadata and state setActive(false) 
- Update metadata and state
Player Implementation Start the player Pause the player Stop the player
Becoming Noisy Register your BroadcastReceiver Unregister your BroadcastReceiver  
Notifications startForeground(notification) stopForeground(false) stopForeground(false)

以下是回调的示例框架:

private IntentFilter intentFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);

// Defined elsewhere...
private AudioManager.OnAudioFocusChangeListener afChangeListener;
private BecomingNoisyReceiver myNoisyAudioStreamReceiver = new BecomingNoisyReceiver();
private MediaStyleNotification myPlayerNotification;
private MediaSessionCompat mediaSession;
private MediaBrowserService service;
private SomeKindOfPlayer player;

MediaSessionCompat.Callback callback = new
MediaSessionCompat.Callback() {
  @Override
  public void onPlay() {
    AudioManager am = mContext.getSystemService(Context.AUDIO_SERVICE);
    // Request audio focus for playback, this registers the afChangeListener
    int result = am.requestAudioFocus(afChangeListener,
                                 // Use the music stream.
                                 AudioManager.STREAM_MUSIC,
                                 // Request permanent focus.
                                 AudioManager.AUDIOFOCUS_GAIN);

    if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
      // Start the service
      service.start();
      // Set the session active  (and update metadata and state)
      mediaSession.setActive(true);
      // start the player (custom call)
      player.start();
      // Register BECOME_NOISY BroadcastReceiver
      registerReceiver(myNoisyAudioStreamReceiver, intentFilter);
      // Put the service in the foreground, post notification
      service.startForeground(myPlayerNotification);
    }
  }

  @Override
  public void onStop() {
    AudioManager am = mContext.getSystemService(Context.AUDIO_SERVICE);
    // Abandon audio focus
    am.abandonAudioFocus(afChangeListener);
    unregisterReceiver(myNoisyAudioStreamReceiver);
    // Start the service
    service.stop(self);
    // Set the session inactive  (and update metadata and state)
    mediaSession.setActive(false);
    // stop the player (custom call)
    player.stop();
    // Take the service out of the foreground
    service.stopForeground(false);
  }

  @Override
  public void onPause() {
    AudioManager am = mContext.getSystemService(Context.AUDIO_SERVICE);
    // Update metadata and state
    // pause the player (custom call)
    player.pause();
    // unregister BECOME_NOISY BroadcastReceiver
    unregisterReceiver(myNoisyAudioStreamReceiver, intentFilter);
    // Take the service out of the foreground, retain the notification
    service.stopForeground(false);
  }
}

注意:如果您使用必要的回调创建您的MediaSession,使用Google Assistant的人可以使用语音命令控制您的应用。 Google Assistant文档中说明了要求。

Android车载应用开发与分析(7)- 车载多媒体(二)- 多媒体应用架构与MediaSession框架 播放音频或视频的多媒体应用通常由两部分组成: 播放器:接收传入的数据多媒体,并输出音频或视频。可以是MediaPlayer、ExoPlayer或其他Player。 界面:用于显示、控制播放. 阅读详情

相关推荐

Android媒体播放框架 MediaSessionCompat与MediaControllerCompat

【代码】Android媒体播放框架 MediaSessionCompat与MediaControllerCompat。

嗯... 2625

关于媒体浏览器服务(MediaBrowserService)

今天说的这个主题与媒体播放有关,尤其是音乐播放,说到音乐播放大家应该都用过音乐App。 通常一个音乐App的实现主要涉及如下几点: 1. 从服务器获取音乐数据 2. 播放音乐时播放器的各种播放状态以及不同状态下的UI展示 3. 播放过程中通过UI界面控制播放器的各种状态 4. UI控制如何与播放服务进行关联并进行状态同步 4. 如何保证后台播放过程中播放服务不被杀死对于上面的这几点,其实

老胡 2万+

Android 多媒体之音频_android wmaextractor

Android中除了MediaPlayer播放音频之外还提供了SoundPool来播放音效,SoundPool使用音效池的概念来管理多个短促的音效,例如它可以开始就加载20个音效,以后在程序中按音效的ID进行播放。需要说明的是,与MediaPlayer相似,使用MediaRecorder录音录像时需要严格遵守状态函数调用的先后顺序,在不同的状态调用不同的函数,否则会出现异常。总结来说,Android中常见的音频压缩格式有:MP3,AAC,OGG,WMA,Opus,FLAC,APE,m4a,AMR,等等。

2401_87298578的博客 2120

Android MediaSession框架简析

q

一杯苦芥的专栏 3277

Android 多媒体之音频

本篇文档,我们用很长的篇幅介绍了多媒体开发中的音频功能的开发和使用,在具体的开发和应用中,重点应该放在对整体知识的理解和架构的梳理上,不要拘泥于某个API的使用,参数的作用等。在开发上,习惯的将音频、视频功能的使用称之为多媒体,实际上如果讲的宽泛一些的话,相机的使用,比如拍照,录制视频等,也可以划分到多媒体的范畴里面。它还支持多种不同的媒体源,比如:。总结来说,Android中常见的音频压缩格式有:MP3,AAC,OGG,WMA,Opus,FLAC,APE,m4a,AMR,等等。

关注我!带你一路 "狂飙" 到底! 1480

安卓手机app开发_媒体内容开发_ExoPlayer管理音频焦点

注意的是,如果你使用ExoPlayer,考虑让ExoPlayer自动地管理音频的焦点,通过 调用setAudioAttributes方法,把handleAudioFocus参数设置为真。使用了这个方法 你的APP就不用包含任何关于请求与响应音频焦点改变的代码了。 两个或者是多个安卓APP能同时播放同一个输出数据流的音频,并且系统把它们 混合在一起了。然而这是技术上让人赞叹的,但它能引起用户的恼火。为了避免 每个音乐APP同时地播放,安卓引入了音频焦点的概念。一个时刻仅有一个APP能 获得音频焦点。

红孩儿编程大师 1842

Android Auto 支持媒体应用程序解析

掌握这些核心知识点,能系统化展示你对 Android Auto 媒体应用开发的深度理解!是 Android Auto 的强制要求,提供标准化内容发现机制。例如:Spotify、YouTube Music、Pandora。例如:Google Podcasts、Overcast。与 Android 媒体框架深度集成(如系统媒体控件)限制首次加载的媒体项数量(例如首批只加载 20 首)支持车机按需加载媒体内容(避免一次性传输全部数据)绑定到主 Activity。每屏最多显示 6 个菜单项。

斗锋在干嘛的博客 885

Android多媒体编程全面指导手册

本文还有配套的精品资源,点击获取 简介:《Android多媒体编程从初学到精通》是一本详尽的技术指南,主要面向Android开发者。该书深入讲解了在Android平台进行多媒体编程的核心技术与实践应用,从基础的音频播放和处理,到高级的视频流媒体、图像处理、相机应用开发。书中不仅包括使用MediaPlayer类、VideoView、SurfaceView和Camera API...

weixin_30923011的博客 1112

android应用程序框架

应用程序框架 Activity Manager:活动管理器。管理所有应用程序生命周期,以及通常的导航返回栈 Window Manager:窗口管理器。管理所有窗口程序 Content Provider:内容提供者。用来让程序之间互相存取、共享数据。例如,某个应用程序可以存取联系人应用程序内的联系人数据 View System:视图系统。用来构建应用程序的基本组件,包含了列表、网格、文本文档

guomeijuan916的专栏 2725

Qt自带绘图与GDI+绘图方式比较_gdi+实时绘制qt(2)

2、GDI+是Windows操作系统中的图形设备接口,它提供了一套用于绘制图形的API,包括了图形渲染、图形变换、图形剪切、图形填充等功能,使开发者可以快速地开发出各种类型的图形应用程序。1、Qt是一个跨平台的C++应用程序框架,它提供了一套用于GUI编程的类库,包括了绘图、网络、XML、数据库、多线程等模块,使开发者可以快速地开发出高质量的跨平台应用程序。Qt提供了一套用于绘图的类库,包括了QPainter、QPen、QBrush等类,使开发者可以快速地进行图形绘制。

qq_17136213的博客 918

Matlab实战:用DQN算法让机器人学会自主避障(附完整代码)

本文详细介绍了如何使用Matlab实现DQN(深度Q网络)算法,使机器人在复杂环境中学会自主避障。通过环境建模、网络架构设计、奖励函数优化及实战调试,提供了完整的代码实现和训练策略,帮助开发者快速应用于机器人路径规划项目。

t8u9v0w1x的博客 234

Android使用webview怎么加载uri_Android 多媒体之音频

在开发上,习惯的将音频、视频功能的使用称之为多媒体,实际上如果讲的宽泛一些的话,相机的使用,比如拍照,录制视频等,也可以划分到多媒体的范畴里面。从本节课开始,我们就来看看Android中多媒体的API使用和具体的功能。本篇文章我们先从音频开发聊起。零、音频开发场景、内容和基本概念在说音频开发之前,我们可以先想一想自己琢磨一下,哪些应用场景会用到音频开发。主要的应用场景大致包括:音频播放器录音机语音...

weixin_39540704的博客 2722

QT 面试题汇总

一、讲述Qt信号槽机制与优势与不足 优点:①类型安全。需要关联的信号槽的签名必须是等同的。即信号的参数类型和参数个数同接受该信号的槽的参数类型和参数个数相同。若信号和槽签名不一致,编译器会报错。 ②松散耦合。信号和槽机制减弱了Qt对象的耦合度。激发信号的Qt对象无需知道是那个对象的那个信号槽接收它发出的信号,它只需在适当的时间发送适当的信号即可,而不需要关心是否被接受和那个对象接受了。Qt就保证了适当的槽得到了调用,即使关联的对象在运行时被删除。程序也不会奔溃。 ...

编程菜鸟 4万+

Android多媒体-音频-重要模块

AudioFlinger 音频流管理由 AudioFlinger::PlaybackThread::Track 实现,Track 与 AudioTrack 是一对一的关系,一个 AudioTrack 创建后,那么 AudioFlinger 会创建一个 Track 与之对应,PlaybackThread 与 AudioTrack/Track 是一对多的关系,一个 PlaybackThread 可以挂着多个 Track。解析audio_effects.conf 文件,得到并加载 系统支持的音效库。

weixin_47145826的博客 2550

Globus数据下载全攻略:从零配置到批量下载大文件(附常见问题解决)

本文详细介绍了Globus数据下载的全流程,从零配置到批量下载大文件的实用技巧,包括环境准备、高级传输策略、性能优化及常见问题解决方案。Globus作为科研数据的高效传输工具,通过并行流传输和自动校验技术,显著提升大规模数据传输的效率和可靠性。

u9v0w的博客 663

保姆级教程:用GMT6.1绘制专业地形起伏图(从数据下载到出图避坑)

本文提供了一份详细的GMT6.1绘制地形起伏图的保姆级教程,从环境搭建到数据下载,再到核心绘图命令解析,帮助用户避开常见错误。特别适合科研人员和地理信息爱好者快速掌握专业地形图的绘制技巧,提升工作效率。

weixin_42549723的博客 278

安卓多媒体开发:音频与视频处理全解析

在移动应用开发中,多媒体功能(音频播放 / 录制、视频播放 / 拍摄)是提升用户体验的核心模块之一。无论是音乐播放器、语音备忘录,还是短视频应用、直播平台,都离不开对音频和视频的处理。Android 系统提供了丰富的 API 支持多媒体开发,从基础的播放功能到复杂的音视频编辑,开发者可以根据需求选择合适的方案。

2401_89387076的博客 957

webrtc+uniapp(h5和app)实现在线聊天功能 uniapp+网页+服务端源码

uniapp端代码 服务端代码 public/index.html

代码简单说 Vue、JAVA、PHP、Node.js 熟练运用,接口、架构、性能全搞定。 755

Android Q(10.0)从其他应用捕获该应用的音频 中文文档

播放捕捉 Android Q包含一个新的AudioPlaybackCapture API。此API使应用程序能够复制其他应用程序正在播放的音频。此功能是屏幕捕获的模拟,但适用于音频。主要用例是用于想要捕获游戏正在播放的音频的流媒体应用。 请注意,捕获API不会影响正在捕获其音频的应用的延迟。 构建捕获应用程序 先决条件 为了安全和隐私,播放捕获带来了一些限制。为了能够捕获音频...

u011046184的博客 6357

【SI案例分享】1000 BASE-T1共模终端的分析与评估

以太网的MDI接口应用中,共模端接网络的应用广泛,并且,其电路参数从100 BASE-T1到1000 BASE-T1保持了相同的参数定义,这就不得不引起使用者的怀疑,毕竟百兆和千兆以太网的工作频段差异巨大,这样的设计真的可以做到上下兼容吗?本文针对此问题,进行了深入的探讨。

一只豌豆象的博客 1367

YOLOv8改进,YOLOv8添加DCNv4可变性卷积(windows系统成功编译),全网最详细教程

DCNv4通过两项关键增强解决了其前身DCNv3的局限性:1. 移除空间聚合中的softmax归一化,以增强其动态特性和表达能力;2. 优化内存访问以最小化冗余操作,从而加速计算。这些改进使得DCNv4相比DCNv3显著加快了收敛速度,并且处理速度大幅提升,前向传播速度超过三倍。

在职AI算法工程师,擅长计算机视觉,YOLO目标检测、分割等,擅长web、pyqt界面可视化,好内容持续更新中,来这里跟大家一起学习,共同进步 6470

安卓之从视频中提取音频的应用场景及技术优劣分析

随着移动设备性能的不断提升和多媒体内容的广泛传播,从视频中提取音频已成为众多开发者与用户日常操作的一部分。在安卓平台上,这项技术经历了从早期的复杂专业工具到现今便捷易用的应用程序的演变过程。本文旨在探讨安卓系统中视频转音频(Video to Audio Extraction, VAE)技术的发展历史、主要应用场景,并对不同实现方法的技术特点和优劣进行深入剖析。

wlred1980的博客 1903

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

Android应用程序结构

一、Android应用程序的架构 Android软件层次结构自上而下可以分为: l         应用程序(Application) l         应用程序框架(Appliciation Framework) l         各种库(Libraries)和Android运行环境(RunTime) l         操作系统层(OS) 二、应用程序        Andr

黑暗之光 3345

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

上一篇: Android媒体应用(三)--使用媒体会话
下一篇: Android媒体应用(五)--构建视频应用程序
ITurbo
博客等级 码龄15年 17粉丝 70原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值