방법: 업로드(실시간)

소개

기본적인 업로드 지식을 바탕으로 에셋이 생성되는 동시에 실시간으로 업로드하는 방법을 살펴보겠습니다. 이 방법을 사용하면 최종 크기를 알기 전에 레코딩, 렌더링, 또는 스트리밍 중에 파일을 업로드할 수 있습니다.

실시간 업로드 API를 사용하면 레코딩이 완료된 후 몇 초 만에 Frame.io에서 에셋을 재생할 수 있으므로 워크플로 효율성이 크게 향상됩니다.

데모 비디오

이 기능에 대한 빠른 미리 보기를 원하시면 비디오 데모를 시청하세요. 이 데모는 Adobe Media Encoder에서 렌더링이 실시간으로 업로드되고 렌더링이 완료된 후 5초 만에 Frame.io에서 재생되는 비디오를 보여줍니다.

사전 요구 사항

아직 C2C 구현. 설정 가이드를 확인하지 않으셨다면 먼저 검토해 주시기 바랍니다. 인증 및 권한 부여 과정에서 획득한 access_token이 필요합니다. 예제에는 기본 업로드 가이드에서 사용했던 것과 동일한 테스트 에셋을 사용하겠습니다. 기본 업로드 가이드의 개념을 바탕으로 진행되므로 해당 내용을 미리 숙지하시는 것이 좋습니다.

실시간 에셋 생성

실시간 업로드는 변경된 에셋 생성 프로세스로 시작됩니다. 에셋을 생성할 때 생성 중에는 최종 크기를 알 수 없으므로 is_realtime_uploadtrue로 설정하고 filesize 매개변수를 생략합니다(또는 null로 설정).

${
>curl -X POST https://api.frame.io/v2/devices/assets \
> --header 'Authorization: Bearer [access_token]' \
> --header 'Content-Type: application/json' \
> --header 'x-client-version: 2.0.0' \
> --data-binary @- <<'__JSON__'
> {
> "name": "C2C_TEST_CLIP.mp4",
> "filetype": "video/mp4",
> "is_realtime_upload": true
> }
>__JSON__
>} | python -m json.tool
API 엔드포인트 사양

/v2/devices/assets에 대한 문서는 여기에서 확인할 수 있습니다.

확장자 및 파일 이름

실시간 에셋에는 파일 확장자가 필요합니다. 에셋을 생성할 때 파일 이름을 모르는 경우 대신 extension 필드를 사용할 수 있습니다(형식: '.mp4'). 이 방식은 나중에 에셋 이름을 업데이트할 계획인 경우에 권장됩니다.

실시간 에셋에 대한 응답은 표준 에셋 생성 시보다 간소화됩니다.

1{
2 "id": "{asset_id}",
3 "name": "C2C_TEST_CLIP.mp4"
4}

upload_urls가 없습니다. 실시간 업로드의 경우 파일이 생성됨에 따라 온디맨드 방식으로 업로드 URL을 생성하기 때문입니다.

업로드 URL 요청

이전 응답의 asset_id를 사용하여 파일의 전반부(10,568,125바이트)에 대한 URL을 요청해 보겠습니다.

${
>curl -X POST https://api.frame.io/v2/devices/assets/{asset_id}/realtime_upload/parts \
> --header 'Authorization: Bearer [access_token]' \
> --header 'Content-Type: application/json' \
> --header 'x-client-version: 2.0.0' \
> --data-binary @- <<'__JSON__'
> {
> "parts": [
> {
> "number": 1,
> "size": 10568125,
> "is_final": false
> }
> ]
> }
>__JSON__
>} | python -m json.tool
API 엔드포인트 사양

/v2/devices/assets/{asset_id}/realtime_upload/parts에 대한 문서는 여기에서 확인할 수 있습니다.

요청 매개변수 이해:

  • parts: URL이 필요한 업로드 파트 목록입니다. 단일 호출로 여러 URL을 요청하면 효율성이 향상됩니다.

  • number: 1부터 시작하는 순차적 파트 번호입니다. 번호를 건너뛰거나 파트를 임의의 순서로 업로드할 수 있지만 조립은 순차적으로 이루어집니다. 10,000(AWS 제한)을 초과할 수 없습니다. * size: 바이트 단위의 파트 크기입니다. AWS 멀티파트 업로드 제한 사항을 준수해야 합니다. * is_final. 최종 파일 파트인지 여부를 나타냅니다.

응답에 요청된 업로드 URL이 포함되어 있습니다.

1{
2 "upload_urls": [
3 "https://frameio-uploads-production.s3-accelerate.amazonaws.com/parts/[part_01_path]"
4 ]
5}

upload_urls 목록은 parts 요청 순서와 직접적으로 일치합니다.

이제 기본 업로드 가이드에서와 같이 첫 번째 청크를 업로드합니다.

$head -c 10568125 ~/Downloads/C2C_TEST_CLIP.mp4 | \
>curl -X PUT https://frameio-uploads-production.s3-accelerate.amazonaws.com/parts/[part_01_path] \
> --include \
> --header 'content-type: video/mp4' \
> --header 'x-amz-acl: private' \
> --data-binary @-

다음으로 두 번째이자 마지막 파트에 대한 URL을 요청합니다.

${
>curl -X POST https://api.frame.io/v2/devices/assets/{asset_id}/realtime_upload/parts \
> --header 'Authorization: Bearer [access_token]' \
> --header 'Content-Type: application/json' \
> --header 'x-client-version: 2.0.0' \
> --data-binary @- <<'__JSON__'
> {
> "asset_filesize": 21136250,
> "parts": [
> {
> "number": 2,
> "size": 10568125,
> "is_final": true
> }
> ]
> }
>__JSON__
>} | python -m json.tool

다음과 같은 중요한 추가 사항에 유의하세요.

  • 마지막 파트에 대해 is_finaltrue로 설정되어 이 청크 이후에 업로드가 완료됨을 알립니다.
  • asset_filesize는 임의의 파트에 is_final: true가 포함된 경우에 필수적인 총 파일 크기를 제공합니다.

응답에서 URL을 받은 후 다음을 수행합니다.

1{
2 "upload_urls": [
3 "https://frameio-uploads-production.s3-accelerate.amazonaws.com/parts/[part_02_path]"
4 ]
5}

최종 청크 업로드:

$tail -c 10568125 ~/Downloads/C2C_TEST_CLIP.mp4 | \
>curl -X PUT https://frameio-uploads-production.s3-accelerate.amazonaws.com/parts/[part_02_path] \
> --include \
> --header 'content-type: video/mp4' \
> --header 'x-amz-acl: private' \
> --data-binary @-
최종 파트 처리

최종 파트가 업로드되면 Frame.io에서 전체 파일을 조립하기 시작합니다. 이 프로세스에는 나머지 파트가 완료될 때까지 대기하는 60초의 유예 기간이 포함되어 있습니다. 다른 모든 파트가 성공적으로 업로드된 후에만 최종 파트를 업로드하는 것이 좋습니다.

다 됐습니다! Frame.io로 이동하여 성공적으로 업로드된 실시간 에셋을 확인하세요. 🎉

에셋 이름 관리

에셋 생성 중에 파일 이름을 모르는 경우 name 없이 extension 필드를 사용할 수 있습니다.

${
>curl -X POST https://api.frame.io/v2/devices/assets \
> --header 'Authorization: Bearer [access_token]' \
> --header 'Content-Type: application/json' \
> --header 'x-client-version: 2.0.0' \
> --data-binary @- <<'__JSON__'
> {
> "extension": ".mp4",
> "filetype": "video/mp4",
> "is_realtime_upload": true
> }
>__JSON__
>} | python -m json.tool

시스템에서 다음과 같은 기본 이름을 할당합니다.

1{
2 "id": "{asset_id}",
3 "name": "[new file].mp4"
4}

업로드 URL을 요청할 때 asset_name 필드를 포함하여 이 이름을 업데이트할 수 있습니다.

${
>curl -X POST https://api.frame.io/v2/devices/assets/{asset_id}/realtime_upload/parts \
> --header 'Authorization: Bearer [access_token]' \
> --header 'Content-Type: application/json' \
> --header 'x-client-version: 2.0.0' \
> --data-binary @- <<'__JSON__'
> {
> "asset_name": "C2C_TEST_CLIP.mp4",
> "asset_filesize": 21136250,
> "parts": [
> {
> "number": 2,
> "size": 10568125,
> "is_final": true
> }
> ]
> }
>__JSON__
>} | python -m json.tool

에셋이 여전히 기본 이름인 경우에만 이름이 업데이트됩니다. Frame.io UI에서 이름이 변경되었거나 이전에 업데이트된 경우 해당 요청은 무시됩니다.

URL 요청 최적화

효율성을 높이려면 개별적으로 요청하는 대신 현재 사용 가능한 데이터가 있는 만큼의 파트에 대한 URL을 요청하세요. 이 방식은 업로드 속도가 데이터 생성 속도보다 뒤처질 수 있는 대용량 파일에 특히 유용합니다.

미디어 파일 헤더 처리

일부 미디어 형식은 전체 파일이 완성될 때까지 기록되지 않는 헤더가 파일 시작 부분에 필요합니다. 이는 헤더가 AWS의 최소 파트 크기인 5MiB(5,242,880바이트)보다 작은 경우 문제를 발생시킵니다.

권장 사항.

  1. 업로드하지 않고 처음 5,242,880바이트의 미디어 데이터를 예약합니다.
  2. part_number=2부터 파트 업로드를 시작합니다.
  3. 파일이 완료되면 예약된 데이터 앞에 헤더를 추가합니다.
  4. part_number=1에 대한 URL을 요청하고 결합된 청크를 업로드합니다.

이 방식을 사용하면 적절한 파일 구조를 유지하면서 첫 번째 청크가 최소 크기 요구 사항을 충족하도록 보장할 수 있습니다.

최적의 성능을 위한 파트 크기 조정

AWS는 업로드 전략에 영향을 미치는 다음과 같은 제한을 둡니다.

  • 최대 파일 크기: 5TiB(5,497,558,138,880바이트)
  • 최대 파트 수: 10,000
  • 최소 파트 크기: 5MiB(5,242,880바이트)

고정된 파트 크기는 다음과 같은 장단점을 만듭니다.

  • 10,000개의 모든 파트에 최소 크기(5MiB)를 사용하면 총 파일 크기가 약 52.4GB로 제한됩니다.
  • 최대 파일 크기를 균등하게 분배하려면 약 550MB의 청크가 필요한데, 이는 작은 파일을 효율적으로 스트리밍하기에는 너무 큽니다.

필요한 경우 매우 큰 파일을 처리할 수 있도록 보장하면서, 업로드 응답성을 높이기 위해 작은 파트부터 시작하여 이러한 제약 조건의 균형을 맞추는 공식이 필요합니다.

권장 파트 크기 공식

Python으로 제안하는 방식은 다음과 같습니다.

Python
1import math
2from typing import Callable
3
4# Constants
5MINIMUM_PART_SIZE = 5_242_880
6MAXIMUM_PART_COUNT = 10_000
7MAXIMUM_FILE_SIZE = 5_497_558_138_880
8
9# Maximum uniform data rate that allows for 10,000 parts
10MAXIMUM_DATA_RATE = MAXIMUM_FILE_SIZE // MAXIMUM_PART_COUNT
11
12def part_size(part_number: int, format_bytes_per_second: int) -> int:
13 """
14 Returns the payload size for a specific part number given the file's
15 expected data rate.
16 """
17 if part_number < 1:
18 raise ValueError("part_number must be greater than 0")
19
20 if part_number > 10_000:
21 raise ValueError("part_number must be less than 10,000")
22
23 # Make sure we never go above the maximum data rate or fall below the
24 # minimum part size, even if the data rate is lower.
25 data_rate = min(format_bytes_per_second, MAXIMUM_DATA_RATE)
26 data_rate = max(data_rate, MINIMUM_PART_SIZE)
27
28 # Calculate a scalar given our data rate. We will explain this step
29 # futher on in the guides.
30 scalar = -(2 * (125 * data_rate - 68_719_476_736)) / 8_334_583_375
31 part_size = math.floor(scalar * pow(part_number, 2)) + data_rate
32
33 return part_size

… 여기서 part_number1에서 10_000 사이(포함)이며, format_bytes_per_second는 파일이 초당 소모할 것으로 예상되는 평균 바이트 수입니다. 이 공식이 어떻게 도출되었는지는 뒤에서 자세히 설명하겠습니다.

스칼라 값

scalar 변수와 계산이 처음에는 약간 당혹스러울 수 있지만, 이는 format_bytes_per_second에 어떤 값을 사용하든 1에서 10_0000까지 허용된 모든 part_number 값을 함수에 입력하면 총합이 5TiB 파일 크기 제한과 정확히(가능한 한 정확하게) 일치하는 값 세트를 얻을 수 있도록 보장하는 수학적 도구입니다. 이 공식을 어떻게 도출했는지에 대한 과정은 뒤에서 추가로 보여드리겠습니다.

내림 처리

내림 처리를 사용하면 약간의 바이트가 남게 되지만, 10,000개 파트에 걸친 일반적인 반올림으로 인해 허용된 최대 파일 크기를 실수로 초과하는 일이 발생하지 않도록 방지합니다. 이 방식으로 최대 10,000바이트(또는 10KB)가 남게 되며, 이는 수용 가능한 트레이드오프입니다.

이 공식의 중요한 특징은 다음과 같습니다.

  • 10,000개의 파트를 업로드할 때 업로드된 총 데이터 양은 5TiB 파일 크기 제한의 10KB 이내가 됩니다.
  • 시작 부분에 작고 효율적인 페이로드에 최적화하여 짧고 중간 길이인 클립의 응답성을 높입니다.
  • 매우 긴 클립은 파일 작성이 완료된 시점과 Frame.io에서 재생할 수 있게 되는 시점 사이의 응답성이 감소합니다.

두 번째와 세 번째 사항 간의 트레이드오프는 대부분의 클립이 세 번째 사항이 적용될 정도의 크기에 도달하지 않는다는 사실로 인해 완화됩니다. 대부분의 파일에서 응답성을 높이는 대신, 극소수 파일의 응답성 감소를 감수하는 것입니다.

정적 스칼라와 데이터 전송률이 미리 계산되어 적용된 익명 part_size_calculator 함수를 생성하는 더 진보하고 효율적인 버전의 공식은 다음과 같습니다.

Python
1def create_part_size_calculator(format_bytes_per_second: int) -> Callable[[int], int]:
2 """
3 Returns a function that takes in a `part_number` and returns a
4 `part_size` based on `data_rate`.
5 """
6
7 # Make sure we never go above the maximum data rate or fall below the
8 # minimum part size, even if the data rate is lower.
9 data_rate = min(format_bytes_per_second, MAXIMUM_DATA_RATE)
10 data_rate = max(data_rate, MINIMUM_PART_SIZE)
11
12 static_scalar = -(2 * (125 * data_rate - 68_719_476_736)) / 8_334_583_375
13
14 def part_size_calculator(part_number: int) -> int:
15 """Calculates size in bytes of upload for `part_number`."""
16 if part_number < 1:
17 raise ValueError("part_number must be greater than 0")
18
19 if part_number > 10_000:
20 raise ValueError("part_number must be less than 10,000")
21
22 return math.floor(static_scalar * pow(part_number, 2) + data_rate)
23
24 return part_size_calculator

공식 성능.

몇 가지 일반적인 파일 형식에서 위 공식의 출력 특성을 살펴보겠습니다.

예시 1: 웹 포맷

전송률이 약 5.3MB/s 이하인 웹 재생 가능 포맷(대부분의 H.264/H.265/HEVC 파일)의 경우 페이로드 크기는 다음과 같이 진행됩니다.

총 파트 수페이로드 바이트페이로드 MB총 파일 바이트 수총 파일 GB
15,242,8965.2MB5,242,8960.0GB
1,00021,575,81721.6MB10,695,361,35710.7GB
5,000413,566,329413.6MB706,957,655,928707.0GB
10,0001,638,536,6791,638.5MB5,497,558,133,9215,497.6GB
표 열 범례 - 총 파트 수: AWS에 업로드된 파일 파트의 총 개수입니다. - Payload Bytes: part_numberTotal Parts와 같을 때의 AWS PUT 페이로드 크기입니다. - Payload MB: Payload Bytes와 동일하지만 메가바이트 단위입니다. - Total File Bytes: Total Parts만큼 순차적인 파트가 업로드되었을 때 파일에 업로드된 총 바이트 수입니다. - Total File GB: Total File Bytes와 동일하지만 GB 단위입니다.

이러한 값은 실시간 업로드, 특히 H.264와 같은 웹 재생 코덱에서 잘 균형을 이룹니다. 대부분은 10.7GB 미만이므로 1,000개의 파트 내에서 완료됩니다. 페이로드 크기는 21.6MB를 절대 초과하지 않습니다.

파트의 절반 정도를 진행한 상황이라도 페이로드 크기는 여전히 413.5MB를 초과하지 않습니다. 업로드되는 총량은 707GB에 달하며, 이는 대부분의 웹 파일에 충분하고도 남습니다.

파일 크기가 급격히 늘어나기 시작하는 것은 허용된 파트 수의 한계에 가까워졌을 때뿐입니다. 하지만 1.7GB를 절대 넘지 않으며, 이는 파트당 AWS 제한인 5GiB를 훨씬 밑도는 수준입니다.

예시 2: ProRes 422 LT. ProRes 422 LT는 102Mbps의 데이터 전송률을 가지며 다음과 같은 표를 생성합니다.

총 파트 수페이로드 바이트페이로드 MB총 파일 바이트 수총 파일 GB
112,750,01612.8MB12,750,0160.0GB
1,00028,857,75828.9MB18,127,308,78318.1GB
5,000415,443,954415.4MB735,107,948,432735.1GB
10,0001,623,525,8171,623.5MB5,497,558,133,9585,497.6GB

이 표는 웹에 최적화된 공식과 비교할 때 유용한 속성을 보여줍니다. 처음 1,000개의 파트 내에서 8GB 더 많은 파일을 업로드할 수 있습니다. 초기 페이로드가 더 크다는 것은 시작 시에 URL을 너무 자주 요청할 필요가 없음을 의미하며, 데이터 전송률이 높을수록 업로드가 더 효율적으로 진행되게 합니다. 업로드 프로세스의 마지막 부분에서도 페이로드 크기는 크게 유지됩니다.

예시 2: 카메라 RAW

마지막으로 데이터 전송률이 280MB/s인 카메라 RAW 포맷을 시도해 보겠습니다. 데이터가 이렇게 빠르게 전송될 때 초반에 5MiB 청크 단위로 업로드를 시도하는 것은 이치에 맞지 않습니다.

총 파트 수페이로드 바이트페이로드 MB총 파일 바이트 수총 파일 GB
1280,000,008280.0MB280,000,0080.3GB
1,000288,091,460288.1MB282,701,200,139282.7GB
5,000482,286,516482.3MB1,737,245,341,5421,737.2GB
10,0001,089,146,0651,089.1MB5,497,558,133,8705,497.6GB

초기 페이로드가 더 효율적일 뿐만 아니라 후반부에서 0.5GB 이상을 절약할 수 있으므로, 네트워크 호출이 악조건인 네트워크 환경의 영향을 덜 받게 됩니다.

도출 과정 살펴보기

모든 것을 예제 업로더로 결합하기 전에 이 공식을 어떻게 도출했는지 살펴보겠습니다.

우리가 해야 할 일은 대부분의 업로드에서는 도달할 일이 없는 허용된 파트 수의 마지막 부분에 크고 무거운 페이로드를 배치하는 대신, 모든 업로드에서 이점을 얻을 수 있도록 초반에는 가볍고 효율적인 페이로드를 사용하게 해주는 공식을 고안하는 것이었습니다. 동시에 알고리즘이 파트 번호 10,000에서 정확히 5TiB 파일 크기 제한에 근접하도록 보장하고자 했습니다.

이제 미적분학을 활용할 차례입니다.

그래프가 기하급수적으로 커지기를 원하므로 공식은 대략 다음과 같아야 합니다.

n2n^2

… 여기서 n은 파트 번호입니다. 또한 각 파트가 최소한 공식의 데이터 전송률(이를 r이라고 함)이 되도록 보장하고자 합니다.

n2+rn^2 + r

이제 처음 10,000개의 자연수(1, 2, 3, …)에 대해 공식의 합을 구할 수 있는 공식을 찾아야 합니다. 시그마 Σ 기호는 합계를 나타냅니다. 공식에 추가해 보겠습니다.

Σxn2+rΣxn^2 + r

… 그리고 n을 1에서 10,000 사이(포함)의 일련의 자연수로 재정의합니다. 이 방정식은 아직 우리에게 그다지 유용하지 않습니다. 직관적으로는 맞는 형태를 갖추고 있지만, 원하는 대로 n=10,000r=5,242,880으로 설정하면 385,812,135,000(385GB)이라는 결과가 도출될 뿐입니다. 결과가 5TiB라는 파일 크기 제한에 한참 못 미칠 뿐만 아니라, 원하는 결과가 나오도록 공식을 조작할 방법도 없습니다.

조절할 수 있는 변수를 하나 추가해 보겠습니다.

Σxn2+rΣxn^2 + r

… 여기서 x는 5TiB라는 결과를 얻기 위해 구할 수 있는 스칼라입니다. 이제 방정식을 파일 크기 제한과 같게 설정하고 x를 구할 수 있습니다.

Σxn2+r=5,497,558,138,880Σxn^2 + r = 5,497,558,138,880

종종 합계는 for 또는 while 루프에서처럼 반복적으로 풀어야 합니다. 하지만 다행히도 우리에게 완벽한 공식이 있습니다. 바로 처음 n개의 자연수에 대한 제곱의 합을 연산 부담 없이 계산하는 잘 알려진 방법입니다.

Σn2=n(n+1)(2n+1)/6Σn^2 = n(n+1)(2n+1)/6

다항식으로 재배열하면 보기가 더 쉬워집니다.

Σn2=(2n3+3n2+n)/6Σn^2 = (2n^3 + 3n^2 + n)/6

양변에 변수 xr을 추가할 수 있습니다.

Σxn2+r=x(2n3+3n2+n)/6+rnΣxn^2 + r = x(2n^3 + 3n^2 + n)/6 + rn

그리고 마지막으로 새 공식을 5TiB와 같게 설정합니다.

x(2n3+3n2+n)/6+rn=5,497,558,138,880x(2n^3 + 3n^2 + n)/6 + rn = 5,497,558,138,880

이제 총 파트 수인 n=10,000을 설정하여 x를 구하기만 하면 됩니다. 이를 통해 주어진 데이터 전송률에 대한 정적 스칼라를 계산할 수 있습니다. 이 작업을 수동으로 수행하는 대신 Wolfram Alpha에 대입해 보겠습니다.

x=(2(125r68719476736))/8334583375x = -(2 (125 r - 68719476736)) / 8334583375

이제 뭔가 실마리가 보입니다! 데이터 전송률이 최소 파트 크기(5MiB)인 경우 다음과 같은 정적 스칼라를 얻게 됩니다.

136,128,233,472/8,334,583,375136,128,233,472 / 8,334,583,375

컴퓨터 환경에서 이는 16.33293799427617이라는 float64 값을 나타냅니다. 이 경우 파트 크기를 결정하는 공식은 다음과 같습니다.

s=16.33293799427617n2+5,242,880s = 16.33293799427617n^2 + 5,242,880

여기서 s는 파트 크기입니다.

아직 문제가 하나 더 있습니다. 실제 환경에서는 바이트가 정수로 떨어지지 않는 페이로드를 가질 수 없습니다. 각 값을 반올림해야 합니다. Python을 사용하여 내림 처리하겠습니다.

Python
1math.floor(16.33293799427617 * pow(part_number, 2)) + 5_242_880

이로써 이 가이드에 제공된 원본 함수의 구체적인 예시에 도달했습니다.

기본 업로더 구축

이 가이드에서 배운 모든 내용을 바탕으로, 실시간으로 렌더링되는 파일을 업로드하기 위한 간단한 Python 형태의 의사 코드를 살펴보겠습니다.

Python
1import math
2from datetime import datetime, timezone
3from typing import Callable
4
5# The minimum size, in bytes, for a single, non-final part upload.
6MINIMUM_PART_SIZE = 5_242_880
7# The maximum filesize in
8MAXIMUM_PART_COUNT = 10_000
9# The maximum size, in bytes, for an AWS upload.
10MAXIMUM_FILE_SIZE = 5_497_558_138_880
11
12# The data rate at which every part is an equal size, and could not
13# be any uniformly larger without violating the maximum total file
14# size if 10_000 parts were to be uploaded. it works out to
15# ~549.8 MB per payload. By enforcing this we actually never need
16# to check if a part exceeds the maximum allowed part size, as our
17# parts will never exceed ~549.8 MB.
18MAXIMUM_DATA_RATE = MAXIMUM_FILE_SIZE // MAXIMUM_PART_COUNT
19
20def create_part_size_calculator(format_bytes_per_second: int) -> Callable[[int], int]:
21 """
22 Returns a function that takes in a `part_number` and returns a
23 `part_size` based on `data_rate`.
24 """
25 ...
26
27def upload_render(data_stream: DataStream, channel: int = 0) -> None:
28 """
29 Uploads an asset for data_stream, which is a custom IO class that pulls remaining
30 upload data from an internal buffer or file, depending on how well the upload is
31 keeping pace with the render.
32
33 Uploads to `channel`
34 """
35
36 asset = c2c.asset_create(
37 extension=data_stream.extension,
38 filetype=data_stream.mimetpye,
39 channel=channel,
40 offset=datetime.now(timezone.utc) - data_stream.created_at()
41 )
42
43 calculate_part_size = create_part_size_calculator(data_stream.data_rate())
44 next_part_number = 0
45
46 while True:
47 next_payload_size = calculate_part_size(next_part_number)
48
49 # Waits until one or more chunks worth of data is ready for upload. Cache
50 # whether our data stream has completed writing the file, and the current
51 # number of bytes we have remaining to upload at this time.
52 available_bytes, stream_complete = data_stream.wait_for_available_data(
53 minimum_bytes=next_payload_size
54 )
55
56 # Build the list of parts to request based on our available data.
57 parts = []
58 while available_bytes > 0:
59 payload_size = calculate_part_size(next_part_number)
60
61 if available_bytes < payload_size and not stream_complete:
62 break
63
64 payload_size = min(payload_size, available_bytes)
65
66 parts.append(
67 c2c.RealtimeUploadPart(
68 part_number=next_part_number,
69 part_size=payload_size,
70 is_final=False
71 )
72 )
73
74 available_bytes -= payload_size
75 next_part_number += 1
76
77 # If our stream is done writing, mark the last part as final.
78 if stream_complete:
79 parts[-1].is_final = True
80
81 # Create the part URLs using the C2C endpoint.
82 response = c2c.create_realtime_parts(
83 asset_id=asset.id,
84 asset_name=None if not stream_complete else data_stream.filename,
85 asset_filesize=None if not stream_complete else data_stream.size(),
86 parts=parts
87 )
88
89 # Upload each part to its URL.
90 for part, part_url in zip(parts, response.upload_urls):
91 part_data = data_stream.read(bytes=part.size)
92 c2c.upload_chunk(part_data, part_url, data_stream.mimetype)
93
94 if stream_complete:
95 break
고급 업로드

위의 코드는 파일을 실시간으로 업로드하는 기본적인 흐름만 보여줍니다. 실제로는 이 로직을 오류 처리고급 업로드 기법으로 보완해야 합니다.

다음 단계

실시간 업로드는 연동 시스템을 최대한 응답성 있게 만들어주며, 레코딩이 완료된 후 불과 몇 초 만에 Frame.io에서 에셋을 재생할 수 있게 해줍니다. 이후 가이드에서는 고급 업로드 기법과 요구 사항을 다룹니다. 기본 업로드를 염두에 두고 작성되었지만, 가이드의 대부분은 여전히 실시간 업로드에도 적용됩니다.

아직 연락하지 않으셨다면 저희 팀에 문의하신 후 다음 가이드를 계속 진행하시기 바랍니다. 여러분의 연락을 기다리겠습니다!