Player Service

[中文]

1. Introduction

The Player Service component is an audio playback component designed and implemented based on the ADK audio development framework.

It supports playing audio data from various sources such as arrays, VFS (Virtual File System), and URLs.

It supports decoding of multiple audio formats, such as G711A/U, MP3, WAV, etc. Since the component is designed based on the audio development framework, customers can adapt other data formats according to their needs.

It supports onboard analog speaker and UAC speaker output.

It supports creating players that play directly and players that do not play directly (decoded PCM data can be output to other components for playback).

2. Software Architecture

The software architecture is shown in the following figure:

Player Service Architecture

Figure Player Service Architecture

The Player Service internally creates a playback pipeline, which consists of an input stream, a decoder, and an output stream.

  • Input Stream: Supports multiple input methods such as array stream, VFS stream, etc., responsible for reading audio source data.

  • Decoder: Supports decoding of multiple audio formats such as G711A/U, MP3, WAV, etc., responsible for decoding compressed audio data into PCM format.

  • Output Stream: Supports onboard speaker and UAC speaker output, responsible for playing PCM data.

The Player Service also provides an event listening mechanism, through which you can register event callback functions to obtain player status changes and music information.

Note

  1. The Player Service supports creating players that do not play directly. In this case, the decoded PCM data can be output to other components for processing by setting the output port.

2. Macro Configuration

Function macro configuration:

Kconfig

CPU

Format

Value

CONFIG_PLAYER_SERVICE

AP

bool

y

CONFIG_PLAYER_SERVICE_SUPPORT_ARRAY_STREAM

AP

bool

y/n

CONFIG_PLAYER_SERVICE_SUPPORT_VFS_STREAM

AP

bool

y/n

CONFIG_PLAYER_SERVICE_SUPPORT_MP3_DECODER

AP

bool

y/n

CONFIG_PLAYER_SERVICE_SUPPORT_G711_DECODER

AP

bool

y/n

CONFIG_PLAYER_SERVICE_SUPPORT_WAV_DECODER

AP

bool

y/n

Dependent function macro configuration:

Kconfig

CPU

Format

Value

CONFIG_ADK

AP

bool

y

CONFIG_ADK_ARRAY_STREAM

AP

bool

y/n

CONFIG_ADK_VFS_STREAM

AP

bool

y/n

CONFIG_ADK_MP3_DECODER

AP

bool

y/n

CONFIG_ADK_G711_DECODER

AP

bool

y/n

CONFIG_ADK_WAV_DECODER

AP

bool

y/n

CONFIG_ADK_ONBOARD_SPEAKER_STREAM

AP

bool

y/n

Note

  1. The meaning of value in macro configuration: y means must be enabled, y/n means choose to enable or not according to actual needs.

  2. If you need to support UAC speaker, MP3 decoder and other functions, refer to the component module description in the audio development framework, and enable the corresponding function macros and component function macros.

3. Test Cases and Reference Demos

The component provides module test cases and reference usage demos:

bk_avdk_smp<source code path>/ap/components/bk_player_service/cli/

The demo is based on the player component and supports playing audio data in arrays and audio files in the VFS file system.

Note

  1. bk_avdk_smp<source code path>/ap/include/components/bk_player_service_types.h provides default configuration references for common solutions, please refer to the API documentation of the Player Service.

4. Typical Examples

Taking a typical application scenario: playing a prompt tone file in an array (using onboard speaker) as an example to illustrate the usage process of the player component.

Creating a Player and Playing a Prompt Tone File

The detailed process is as follows:
  1. Configure player parameters and create a player handle.

  2. Configure URI information and set the URI of the player.

  3. Set the decoding type (only required when playing array audio files).

  4. Start the player to play the prompt tone file.

The following is a code example

/* 1. Configure player parameters and create a player handle */
bk_player_cfg_t player_cfg = DEFAULT_PLAYER_WITH_PLAYBACK_CONFIG();
player_cfg.spk_cfg.onboard_spk_cfg.sample_rate = 8000;
player_cfg.event_handle = player_event_handler;  // Register event callback function
player_cfg.args = NULL;

bk_player_handle_t player_handle = bk_player_create(&player_cfg);
if (!player_handle)
{
    LOGE("player create fail\n");
    return BK_FAIL;
}

/* 2. Configure URI information and set the URI of the player. */
player_uri_info_t uri_info = {0};
uri_info.uri_type = PLAYER_URI_TYPE_ARRAY;
uri_info.uri = (char *)audio_data_array;  // Audio data array pointer
uri_info.total_len = sizeof(audio_data_array);  // Total length of audio data

if (BK_OK != bk_player_set_uri(player_handle, &uri_info))
{
    LOGE("player set uri fail\n");
    return BK_FAIL;
}

/* 3. Set the decoding type (only required when playing array audio files) */
if (BK_OK != bk_player_set_decode_type(player_handle, AUDIO_DEC_TYPE_PCM))
{
    LOGE("player set decode type fail\n");
    return BK_FAIL;
}

/* 4. Start the player to play the prompt tone file */
if (BK_OK != bk_player_start(player_handle))
{
    LOGE("player start fail\n");
    return BK_FAIL;
}

Note

  1. When playing audio files in arrays, the decoding type cannot be determined by the file extension, so you need to set the decoding type through the bk_player_set_decode_type function.

  2. When playing audio files in VFS, the decoding type can be determined by parsing the file extension, so there is no need to set the decoding type through the bk_player_set_decode_type function.

Stopping Playback and Destroying the Player

The detailed process is as follows:
  1. Stop playback.

  2. Destroy the player.

The following is a code example

/* 1. Stop playback */
if (BK_OK != bk_player_stop(player_handle))
{
    LOGE("player stop fail\n");
    return BK_FAIL;
}

/* 2. Destroy the player */
if (BK_OK != bk_player_destroy(player_handle))
{
    LOGE("player destroy fail\n");
    return BK_FAIL;
}

5. Interrupt Playback Example

Interrupt playback application scenario: While playing other audio streams (such as voice calls, Bluetooth music, etc.), intersperse playing prompt tone files (such as low battery, navigation prompt tones, etc.).

Taking playing a prompt tone (prompt tone in VFS) during a voice call as an example to illustrate the usage process of the player component.

Creating a Non-Direct Playback Player and Playing a Prompt Tone File

The detailed process is as follows:
  1. Configure and start a voice call.

  2. Configure player parameters without playback function and create a player handle.

  3. Create and set the output port of the player.

  4. Bind the output port of the player to the input port of the voice call playback module.

  5. Configure URI information and set the URI of the player.

  6. Start the player to play the prompt tone file.

The following is a code example

/* 1. Configure and start a voice call */
voice_cfg_t voice_cfg = DEFAULT_VOICE_BY_ONBOARD_MIC_SPK_CONFIG();
voice_handle_t voice_handle = bk_voice_init(&voice_cfg);

voice_read_cfg_t voice_read_cfg = VOICE_READ_CFG_DEFAULT();
voice_read_cfg.voice_handle = voice_handle;
voice_read_cfg.max_read_size = 1280;
voice_read_cfg.voice_read_callback = doorbell_udp_voice_send_callback;  //doorbell_udp_voice_send_callback mic data caalback
voice_read_handle_t voice_read_handle = bk_voice_read_init(&voice_read_cfg);

voice_write_cfg_t voice_write_cfg = VOICE_WRITE_CFG_DEFAULT();
voice_write_cfg.voice_handle = voice_handle;
voice_write_handle_t voice_write_handle = bk_voice_write_init(&voice_write_cfg);

bk_voice_start(voice_handle);
bk_voice_read_start(voice_read_handle);
bk_voice_write_start(voice_write_handle);

/* 2. Configure player parameters without playback function and create a player handle */
bk_player_cfg_t player_cfg = DEFAULT_PLAYER_NOT_PLAYBACK_CONFIG();
player_cfg.event_handle = player_event_handler;  // Register event callback function
player_cfg.args = NULL;
bk_player_handle_t player_handle = bk_player_create(&player_cfg);
if (!player_handle)
{
    LOGE("player create fail\n");
    return BK_FAIL;
}

/* 3. Create and set the output port of the player */
ringbuf_port_cfg_t cfg = RINGBUF_PORT_CFG_DEFAULT();
cfg.ringbuf_size = OUTPUT_RB_SIZE;
audio_port_handle_t output_port_handle = ringbuf_port_init(&cfg);   //Create corresponding type port according to player output type (ringbuffer, framebuffer, callback)
if (!output_port_handle)
{
    LOGE("output port init fail\n");
    return BK_FAIL;
}

if (BK_OK != bk_player_set_output_port(player_handle, output_port_handle))
{
    LOGE("player set output port fail\n");
    return BK_FAIL;
}

/* 4. Bind the output port of the player to the input port of the voice call playback module */
audio_element_handle_t spk_element = bk_voice_get_spk_element(voice_handle);
if (spk_element == NULL)
{
    LOGE("%s, %d, bk_voice_get_spk_element fail\n", __func__, __LINE__);
    return BK_FAIL;
}

audio_port_info_t port_info = DEFAULT_AUDIO_PORT_INFO();
port_info.chl_num = 2;              // Configure according to prompt tone format
port_info.sample_rate = 48000;      // Configure according to prompt tone format
port_info.dig_gain = 0x2d;          // Configure according to prompt tone format
port_info.ana_gain = 0x01;          // Configure according to prompt tone format
port_info.bits = 16;                // Configure according to prompt tone format
port_info.port_id = 1;
port_info.priority = 1;
port_info.port = output_port_handle;
port_info.notify_cb = player_port_state_notify_handler;
port_info.user_data = NULL;
if (BK_OK != onboard_speaker_stream_set_input_port_info(spk_element, &port_info))
{
    LOGE("%s, %d, audio_element_set_multi_input_port fail\n", __func__, __LINE__);
    return BK_FAIL;
}

/* 5. Configure URI information and set the URI of the player */
player_uri_info_t uri_info = {0};
uri_info.uri_type = PLAYER_URI_TYPE_VFS;    // Prompt tone storage type
uri_info.uri = "/sd0/doorbell.wav";         // Audio file path
if (BK_OK != bk_player_set_uri(player_handle, &uri_info))
{
    LOGE("player set uri fail\n");
    return BK_FAIL;
}

/* 6. Start the player to play the prompt tone file */
if (BK_OK != bk_player_start(player_handle))
{
    LOGE("player start fail\n");
    return BK_FAIL;
}

Note

  1. Step 4. Bind the player to the output port of the voice call requires configuring the audio format information for playback. Therefore, if you are not sure about the audio format information, you can execute this step in the callback function registered when creating the player (the audio format information will be reported after successful decoding).

  2. For the use of voice call components, please refer to: Voice Service Developer Guide.

  3. For the use of multi-source playback function, please refer to: Audio Streams Developer Guide.

Stopping Playback and Destroying the Player

The detailed process is as follows:
  1. Stop the player playback.

  2. Unbind the output port of the player from the input port of the voice call playback module.

  3. Unbind the output port of the player from the player and destroy it.

  4. Destroy the player.

The following is a code example

/* 1. Stop the player playback */
if (BK_OK != bk_player_stop(player_handle))
{
    LOGE("player stop fail\n");
    return BK_FAIL;
}

/* 2. Unbind the output port of the player from the input port of the voice call playback module */
audio_port_info_t port_info = DEFAULT_AUDIO_PORT_INFO();
port_info.chl_num = 2;
port_info.sample_rate = 48000;
port_info.dig_gain = 0x2d;
port_info.ana_gain = 0x01;
port_info.bits = 16;
port_info.port_id = 1;
port_info.priority = 1;
port_info.port = NULL;
port_info.notify_cb = NULL;
port_info.user_data = NULL;
if (BK_OK != onboard_speaker_stream_set_input_port_info(spk_element, &port_info))
{
    LOGE("%s, %d, audio_element_set_multi_input_port fail\n", __func__, __LINE__);
    return BK_FAIL;
}

/* 3. Unbind the output port of the player from the player and destroy it */
bk_player_set_output_port(player_handle, NULL);

audio_port_deinit(output_port_handle);

/* 4. Destroy the player */
if (BK_OK != bk_player_destroy(player_handle))
{
    LOGE("player destroy fail\n");
    return BK_FAIL;
}