# Quickstart (/en/realtime-media/media-pull/get-started/enable-media-pull)

> For AI agents: see the complete documentation index at [llms.txt](/llms.txt).

Media Pull service enables you to inject external media streams into real-time Agora channels. This page explains how to use Media Pull and Video SDK to publish external media to a channel.

## Prerequisites [#prerequisites]

Before starting, ensure that you have implemented the [Video SDK quickstart](/en/realtime-media/video/get-started-sdk) in your project.

## Implement Media Pull [#implement-media-pull]

This section guides you through the implementation of Media Pull basic features in your app.

### Enable Media Pull [#enable-media-pull]

To enable Media Pull, take the following steps:

1. Log in to [Agora Console](https://console.agora.io/v2), and click **Projects** in the left navigation panel.

2. On the **My Projects** page, find the project for which you want to enable Media Pull and click **Edit**.

   ![](https://assets-docs.agora.io/images/console/console-edit-project.png)

3. Under **All Features**, select **Media Pull**.

4. Click **Enable**. If you do not see the **Enable** button, [contact support](mailto\:support@agora.io).

You can view usage statistics on the Agora Console **Usage** page.

### Create a Cloud Player [#create-a-cloud-player]

To pull external media into the channel, you create a cloud player using the [Create RESTful API](../reference/restful-api#create-api). Refer to the following points when calling the API:

* **Region configuration**:
  * Set the `region` parameter to match the location of your media streaming source. For example, use `na` if the source is in North America.
  * Ensure the `region` value is in lowercase.
* **Request header**:
  * Assign a unique string to the `X-Request-ID` field for troubleshooting. The Agora server returns an `X-Custom-request-ID` field in the response header.
* **User identification**:
  * Choose either `UID` or `account` for the cloud player's user name; never set both.
  * Ensure each cloud player in a channel has a unique user name.
* **Avoid duplicates**:
  * Use the `name` field to manage cloud players. Names must be unique within a project. Attempting to create a cloud player with a duplicate name returns a `409 (Conflict)` status code.
* **Supported formats**:
  * Ensure that the media stream uses supported audio/video formats and protocols to avoid failures.
* **Idle timeout**:
  * Set the `idleTimeout` parameter to a suitable value. The default value of 300 seconds is recommended. The cloud player automatically destroys the stream after this period of inactivity.
* **Cloud player creation events**:
  * Upon successful creation, the message notification server sends a payload with the `status` field set to `connecting`, indicating the server is connecting to the media stream or probing audio and video data.

Refer to the [RESTful API Reference](../reference/restful-api) to update player settings or to delete a cloud player.

### Query Cloud Players [#query-cloud-players]

To retrieve a list of cloud players under a project, use the [List RESTful API](../reference/restful-api#list-api). Refer to the following points when calling the API:

1. For the first query, omit the `pageToken` parameter. The response includes the current page and a `nextPageToken`.
2. Use the `nextPageToken` in subsequent queries to retrieve the next page from the list of cloud players.
3. When `nextPageToken` is `0`, all cloud players under the project have been listed.

Cloud players are returned in ascending order based on their creation time (`createTs`).

### Subscribe to a cloud stream [#subscribe-to-a-cloud-stream]

Once the cloud player is successfully created, it joins your channel as a virtual user with the specified `uid`. You can now subscribe to this user's stream in your app.

<Tabs items="[&#x22;Android&#x22;, &#x22;iOS&#x22;, &#x22;macOS&#x22;, &#x22;Web&#x22;, &#x22;Flutter&#x22;]">
  <Tab>
    ```java
    rtcEngine.setEventHandler(new IRtcEngineEventHandler() {
        @Override
        public void onUserPublished(int uid, int mediaType) {
            super.onUserPublished(uid, mediaType);

            // Subscribe to the user's media stream
            rtcEngine.subscribe(uid, mediaType);
        }
    });
    ```
  </Tab>

  <Tab>
    ```swift
    // Callback when a remote user publishes a stream
    func rtcEngine(_ engine: AgoraRtcEngineKit, didUserPublish uid: UInt, with mediaType: AgoraMediaType) {
        print("User published: \(uid), mediaType: \(mediaType)")

        // Subscribe to the user's media stream
        agoraEngine.subscribeStream(uid, mediaType: mediaType)
    }

    ```
  </Tab>

  <Tab>
    ```swift
    // Callback when a remote user publishes a stream
    func rtcEngine(_ engine: AgoraRtcEngineKit, didUserPublish uid: UInt, with mediaType: AgoraMediaType) {
        print("User published: \(uid), mediaType: \(mediaType)")

        // Subscribe to the user's media stream
        agoraEngine.subscribeStream(uid, mediaType: mediaType)
    }
    ```
  </Tab>

  <Tab>
    ```javascript
    client.on("user-published", async (user, mediaType) => {
        await client.subscribe(user, mediaType);
        if (mediaType === "video") {
            const remoteVideoTrack = user.videoTrack;
            remoteVideoTrack.play("remote-video-container"); // Replace with your container element
        } else if (mediaType === "audio") {
            const remoteAudioTrack = user.audioTrack;
            remoteAudioTrack.play();
        }
    });
    ```
  </Tab>

  <Tab>
    ```dart
    // Set up event handlers
    _engine.registerEventHandler(
        RtcEngineEventHandler(
            onUserPublished: (int uid, MediaType mediaType, int elapsed) {
                print("User published: $uid, mediaType: $mediaType");
                // Subscribe to the user's media stream
                _engine.subscribeStream(uid: uid, mediaType: mediaType);
            }
        )
    );
    ```
  </Tab>
</Tabs>
