# Push Publishing

OvenMediaEngine Enterprise는 Live Stream을 다른 System으로 Re-streaming할 수 있는 Push Publishing 기능을 제공합니다. 지원 Protocol은 널리 사용되는 SRT, RTMP, MPEG-2 TS입니다.

OvenMediaEngine의 `StreamMap` 기능을 활용하여 사전 정의된 조건에 따라 Re-streaming을 자동으로 수행할 수 있습니다. 또한, REST API을 통한 Controling 및 Monitoring을 지원합니다.

## Configuring Push Publishing

### Push Publisher

Push Publishing을 사용하려면 `Server.xml` 내 `<Applications><Publishers>`에서 `<Push>`를 설정해야합니다.

```xml
<Applications>
  <Application>
     ...
    <Publishers>
      ... 
      <Push>
         <!-- [Optional] -->
         <StreamMap>
           <Enable>true</Enable>
           <Path>push_info.xml</Path>
         </StreamMap>
         <!-- Connection Timeout -->
         <ConnectionTimeout>1000</ConnectionTimeout>
         <!-- Send Timeout -->
         <SendTimeout>100</SendTimeout>
      </Push>
      ...
    </Publishers>
  </Application>
</Applications>
```

{% hint style="info" %}
RTMP Protocol은 H264 (Video Codec)와 AAC (Audio Codec)만 지원합니다.
{% endhint %}

OvenMediaEngine Enterprise 0.20.2.1-1 버전부터 Push Publisher에서 `ConnectionTimeout`과 `SendTimeout` 옵션을 사용할 수 있습니다.

<table><thead><tr><th width="249.5555419921875">Element</th><th>Description</th></tr></thead><tbody><tr><td><code>ConnectionTimeout</code></td><td><p>Server와의 연결을 시도할 때 허용되는 최대 대기 시간입니다.</p><ul><li>단위: ms</li></ul></td></tr><tr><td><code>SendTimeout</code></td><td><p>연결된 상태에서 Data를 전송할 때 허용되는 최대 대기 시간입니다.</p><ul><li>단위: ms</li></ul></td></tr></tbody></table>

### StreamMap

`<StreamMap>`은 사용자 정의 조건에 따라 콘텐츠를 자동으로 Push Publishing하기 위해 사용합니다. XML File Path는 `<ApplicationPath>/conf`를 기준으로 한 상대 경로를 지정해야 합니다.

```xml
<?xml version="1.0" encoding="UTF-8"?>
<PushInfo>
  <Push>
    <Enable>true</Enable>
    <StreamName>stream_a_*</StreamName>
    <VariantNames>video_h264,audio_aac</VariantNames>
    <Protocol>rtmp</Protocol>
    <Url>rtmp://1.2.3.4:1935/app/${SourceStream}</Url>
    <StreamKey></StreamKey>
    <TimestampMode>ZeroBased</TimestampMode>
  </Push>
  <Push>
    <Enable>true</Enable>
    <StreamName>stream_b_*</StreamName>
    <VariantNames></VariantNames>
    <Protocol>srt</Protocol>
    <Url>srt://1.2.3.4:9999?streamid=srt%3A%2F%2F1.2.3.4%3A9999%2Fapp%2Fstream</Url>
    <TimestampMode>Original</TimestampMode>
  </Push>
  <Push>
    <Enable>false</Enable>
    <StreamName>stream_c_*</StreamName>
    <VariantNames></VariantNames>
    <Protocol>mpegts</Protocol>
    <Url>udp://1.2.3.4:2400</Url>
  </Push>
</PushInfo>
```

#### XML Elements

<table><thead><tr><th width="189.5555419921875">Element</th><th width="137.8887939453125" align="center">Requirement</th><th>Description</th></tr></thead><tbody><tr><td><code>&#x3C;StreamName></code></td><td align="center">Required</td><td><p>Output Stream을 매칭하는 데 사용합니다.</p><ul><li>Wildcard characters (<code>*</code>)를 지원합니다.</li></ul></td></tr><tr><td><code>&#x3C;VariantNames></code></td><td align="center">Required</td><td><p>특정 Track을 선택할 때 사용합니다. <code>&#x3C;VariantNames></code>를 지정하지 않으면 기본적으로 모든 Track이 전송됩니다.</p><ul><li>여러 Variant는 콤마(<code>,</code>)로 구분하여 지정할 수 있습니다.</li></ul></td></tr><tr><td><code>&#x3C;Protocol></code></td><td align="center">Required</td><td><p>지원 Protocol은 <code>srt</code>, <code>mpegts</code>, <code>rtmp</code>입니다.</p><ul><li><code>SRT</code> 또는 <code>MPEG-TS</code> 전송 시, Network 및 환경(예: Firewall, Port, Packet Loss 등)을 고려해야 합니다.</li><li><code>RTMP</code> 전송 시, H.264 + AAC 사용을 권장합니다.</li></ul></td></tr><tr><td><code>&#x3C;Url></code></td><td align="center">Required</td><td><p>Destination Address를 지정합니다.</p><ul><li>Macro를 지원합니다.</li></ul></td></tr><tr><td><code>&#x3C;StreamKey></code></td><td align="center">Optional</td><td><p>필요 시 Stream Key를 지정합니다 (<code>RTMP</code>). </p><ul><li>Macro를 지원합니다.</li></ul></td></tr><tr><td><code>&#x3C;TimestampMode></code></td><td align="center"><p>Optional</p><p><sub><em><mark style="color:yellow;">(Enterprise Only)</mark></em></sub></p></td><td><p>Outgoing Packet의 Timestamp Mode를 설정합니다.</p><ul><li><code>ZeroBased</code>: 전송 시작 시점부터 Timestamp를 0으로 설정하여 전달합니다.</li><li> <code>Original</code> (default): 원본 Stream과 동일한 Timestamp를 전달합니다.</li></ul></td></tr></tbody></table>

#### Macros

자동화 및 운영 효율 향상을 위해 Macro 활용을 권장합니다.

<table><thead><tr><th width="249.5555419921875">Macro</th><th>Description</th></tr></thead><tbody><tr><td>${Application}</td><td>Application name</td></tr><tr><td>${SourceStream}</td><td>Source stream name</td></tr><tr><td>${Stream}</td><td>Output stream name</td></tr></tbody></table>

## API Interface

### Start Push Publishing

> **Request**

<details>

<summary><mark style="color:blue;">POST</mark> /v1/vhosts/{vhost}/apps/{app}:startPush</summary>

**Header**

```http
Authorization: Basic {credentials}

# Authorization
    Credentials for HTTP Basic Authentication created with <AccessToken>
```

**Body : SRT**

```json
{
  "id": "{unique_push_id}",
  "stream": {
    "name": "{output_stream_name}",
    "variantNames": []
  },
  "protocol": "srt",
  "url": "srt://{host}[:port]?mode=caller&latency=120000&timeout=500000",
  "streamKey": ""
}

# id (required)
    unique ID to identify the task
    
# stream (required)
    ## name (required)
        output stream name
        
    ## variantNames (optional)
        Array of track names to publsh. 
        This value is Encodes.[Video|Audio|Data].Name in the OutputProfile
        setting.
        
        If empty, all tracks will be sent.

# protocol (required)
    srt
    
# url (required) 
    address of destination.
    options can be set in query-string format.
    
# streamKey (optional)
    not used with mpegts
```

In SRT Push Publisher, only the `caller` connection mode is supported.

**Body : RTMP**

{% code overflow="wrap" %}

```json
{
  "id": "{unique_push_id}",
  "stream": {
    "name": "{output_stream_name}",
    "variantNames": [ "h264_fhd", "aac" ]
  },
  "protocol": "rtmp",
  "url":"rtmp://{host}[:port]/{app_name}",
  "streamKey":"{stream_name}"
}

# id (required)
    unique ID to identify the task
    
# stream (required)
    ## name (required)
        output stream name
        
    ## variantNames (optional)
        Array of track names to publsh. 
        This value is Encodes.[Video|Audio|Data].Name in the OutputProfile
        setting.
        
        If empty, The first track among video tracks (by ID) and the first 
        track among audio tracks (by ID) are selected automatically.

# protocol (required)
    rtmp
    
# url (required) 
    address of destination
    
# streamKey (required)
    RTMP stream key
```

{% endcode %}

**Body : MPEG2-TS**

```json
{
  "id": "{unique_push_id}",
  "stream": {
    "name": "{output_stream_name}",
    "variantNames": []
  },
  "protocol": "mpegts",
  "url": "udp://{host}[:port]",
  "streamKey": ""
}

# id (required)
    unique ID to identify the task
    
# stream (required)
    ## name (required)
        output stream name
        
    ## variantNames (optional)
        Array of track names to publsh. 
        This value is Encodes.[Video|Audio|Data].Name in the OutputProfile
        setting.
        
        If empty, all tracks will be sent.

# protocol (required)
    mpegts
    
# url (required) 
    address of destination
    
# streamKey (optional)
    not used with mpegts
```

</details>

> **Responses**

<details>

<summary><mark style="color:blue;">200</mark> Ok</summary>

The request has succeeded

**Header**

```
Content-Type: application/json
```

**Body**

Please note that `responses` are incorrectly returned in Json array format for version 0.15.3 and earlier.

```json
{
    "statusCode": 200,
    "message": "OK",
    "response": {
        "id": "{unique_push_id}",
        "state": "ready",
            
        "vhost": "default",
        "app": "app",
        "stream": {
            "name": "{output_stream_name}",
            "trackIds": [],
            "variantNames": []
        },
            
        "protocol": "rtmp",
        "url": "rtmp://{host}[:port]/{app_name}",
        "streamKey": "{stream_name}",
            
        "sentBytes": 0,
        "sentTime": 0,
        "sequence": 0,
        "totalsentBytes": 0,
        "totalsentTime": 0,
            
        "createdTime": "2023-03-15T23:02:34.371+09:00",
        "startTime": "1970-01-01T09:00:00.000+09:00",
        "finishTime": "1970-01-01T09:00:00.000+09:00"
    }
}

# statusCode
    Same as HTTP Status Code
# message
    A human-readable description of the response code
# response
    Created push publishing task information
```

</details>

<details>

<summary><mark style="color:red;">400</mark> Bad Request</summary>

Invalid request.

</details>

<details>

<summary><mark style="color:red;">401</mark> Unauthorized</summary>

Authentication required

**Header**

```http
WWW-Authenticate: Basic realm=”OvenMediaEngine”
```

**Body**

```json
{
    "message": "[HTTP] Authorization header is required to call API (401)",
    "statusCode": 401
}
```

</details>

<details>

<summary><mark style="color:red;">404</mark> Not Found</summary>

The given vhost or application name could not be found.

**Body**

```json
{
    "message": "[HTTP] Could not find the application: [vhost/app1] (404)",
    "statusCode": 404
}
```

</details>

<details>

<summary><mark style="color:red;">409</mark> Conflict</summary>

duplicate ID

</details>

<table><thead><tr><th width="189.5555419921875">Parameter</th><th width="138.4444580078125" align="center">Requirement</th><th>Description</th></tr></thead><tbody><tr><td><code>id</code></td><td align="center">Required</td><td><p>Push Publishing Task를 식별하기 위한 고유 ID 문자열입니다.</p><ul><li>ID 중복은 허용되지 않습니다.</li></ul></td></tr><tr><td><code>stream</code></td><td align="center">Required</td><td>Output Target Stream Information Object입니다.</td></tr><tr><td><code>stream.name</code></td><td align="center">Required</td><td><p>Output Stream의 Name입니다.</p><ul><li>예시: <code>"myStreamOut"</code>.</li></ul></td></tr><tr><td><code>stream.variantNames</code></td><td align="center">Optional</td><td><p>전송할 Track (Variant) Name 목록입니다.</p><ul><li><code>variantNames</code>를 생략하거나 지정하지 않으면, 모든 Track이 전송되거나, <code>RTMP</code>의 경우 첫번째 Video Track과 Audio Track이 선택됩니다.</li></ul></td></tr><tr><td><code>protocol</code></td><td align="center">Required</td><td><p>Push Publishing에 사용할 Protocol입니다.</p><ul><li>예시: <code>"srt"</code>, <code>"mpegts"</code>, <code>"rtmp"</code>.</li></ul></td></tr><tr><td><code>url</code></td><td align="center">Required</td><td><p>Destination URL입니다.</p><ul><li>Protocol별로 형식이 다르며 Query String 옵션을 사용할 수 있습니다.</li></ul></td></tr><tr><td><code>streamKey</code></td><td align="center">Optional</td><td><p><code>RTMP</code> 전송 시 필요한Stream Key입니다.</p><ul><li><code>MPEG-TS</code> 전송 시, 생략할 수 있습니다.</li></ul></td></tr><tr><td><code>timestampMode</code></td><td align="center"><p>Optional</p><p><sub><em><mark style="color:yellow;">(Enterprise Only)</mark></em></sub></p></td><td><p>Outgoing Packet의 Timestamp Mode를 설정합니다.</p><ul><li><code>ZeroBased</code>: 전송 시작 시점부터 Timestamp를 0으로 설정하여 전달합니다.</li><li> <code>Original</code> (default): 원본 Stream과 동일한 Timestamp를 전달합니다.</li></ul></td></tr></tbody></table>

### Stop Push Publishing

> **Request**

<details>

<summary><mark style="color:blue;">POST</mark> /v1/vhosts/{vhost}/apps/{app}:stopPush</summary>

**Header**

```http
Authorization: Basic {credentials}

# Authorization
    Credentials for HTTP Basic Authentication created with <AccessToken>
```

**Body**

{% code overflow="wrap" %}

```json
{
    "id": "{unique_push_id}"
}

# id (required)
    unique ID to identify the push publishing task
```

{% endcode %}

</details>

> **Responses**

<details>

<summary><mark style="color:blue;">200</mark> Ok</summary>

The request has succeeded

**Header**

```
Content-Type: application/json
```

**Body**

```json
{
    "statusCode": 200,
    "message": "OK",
}

# statusCode
	Same as HTTP Status Code
# message
	A human-readable description of the response code
```

</details>

<details>

<summary><mark style="color:red;">400</mark> Bad Request</summary>

Invalid request.

</details>

<details>

<summary><mark style="color:red;">401</mark> Unauthorized</summary>

Authentication required

**Header**

```http
WWW-Authenticate: Basic realm=”OvenMediaEngine”
```

**Body**

```json
{
    "message": "[HTTP] Authorization header is required to call API (401)",
    "statusCode": 401
}
```

</details>

<details>

<summary><mark style="color:red;">404</mark> Not Found</summary>

The given vhost/application name or id of recording task could not be found.

**Body**

```json
{
    "message": "[HTTP] Could not find the application: [vhost/app1] (404)",
    "statusCode": 404
}
```

</details>

### Get Push Publishing State

> **Request**

<details>

<summary><mark style="color:blue;">POST</mark> /v1/vhosts/{vhost}/apps/{app}:pushes</summary>

**Header**

```http
Authorization: Basic {credentials}

# Authorization
    Credentials for HTTP Basic Authentication created with <AccessToken>
```

**Body**

{% code overflow="wrap" %}

```json
{
    "id": "{unique_push_id}"
}

# id (optional)
    unique ID to identify the push publishing task. If no id is given in the request, the full list is returned.
```

{% endcode %}

</details>

> **Responses**

<details>

<summary><mark style="color:blue;">200</mark> Ok</summary>

The request has succeeded

**Header**

```
Content-Type: application/json
```

**Body**

The `response` is <mark style="color:green;">Json array</mark> format.

```json
{
    "statusCode": 200,
    "message": "OK",
    "response": [
        {
            "id": "{unique_push_id}",
            "state": "started",
            
            "vhost": "default",
            "app": "app",
            "stream": {
                "name": "{output_stream_name}",
                "trackIds": [],
                "variantNames": []
            },
            
            "protocol": "rtmp",
            "url": "rtmp://{host}[:port]/{app_name}",
            "streamKey": "{stream_name}",
            
            "sentBytes": 0,
            "sentTime": 0,
            "sequence": 0,
            "totalsentBytes": 0,
            "totalsentTime": 0,
            
            "createdTime": "2023-03-15T23:02:34.371+09:00",
            "startTime": "1970-01-01T09:00:00.000+09:00",
            "finishTime": "1970-01-01T09:00:00.000+09:00"
        },
        {
            "id": "4",
            ...
        }
    ]
}

# statusCode
	Same as HTTP Status Code
# message
	A human-readable description of the response code
# response
	Information of recording tasks. If there is no recording task, 
	response with empty array ("response": [])
```

</details>

<details>

<summary><mark style="color:red;">401</mark> Unauthorized</summary>

Authentication required

**Header**

```http
WWW-Authenticate: Basic realm=”OvenMediaEngine”
```

**Body**

```json
{
    "message": "[HTTP] Authorization header is required to call API (401)",
    "statusCode": 401
}
```

</details>

<details>

<summary><mark style="color:red;">404</mark> Not Found</summary>

The given vhost or application name could not be found.

**Body**

```json
{
    "message": "[HTTP] Could not find the application: [vhost/app1] (404)",
    "statusCode": 404
}
```

</details>

#### State of Push Publishing

<table><thead><tr><th width="249.5555419921875">State</th><th>Description</th></tr></thead><tbody><tr><td>ready</td><td>스트림이 생성되기를 기다리는 중입니다.</td></tr><tr><td>connecting</td><td>대상(destination)에 연결 중입니다.</td></tr><tr><td>pushing</td><td>연결되어 스트리밍 중입니다.</td></tr><tr><td>stopping</td><td>연결 해제 / 중지 작업이 진행 중입니다.</td></tr><tr><td>stopped</td><td>Push 연결이 해제되었거나 중지되었습니다.</td></tr><tr><td>error</td><td>Push 중 오류가 발생했습니다.</td></tr></tbody></table>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ovenmediaengine-enterprise.gitbook.io/guide/ko-kr/features/streaming-and-distribution/push-publishing.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
