VOD Streaming Server 구축 4 (AWS)

ASHAPPYASIKNOW·2022년 7월 18일
0

AWS

목록 보기
4/6
post-thumbnail

구축 계획

  1. 수동으로 input S3에 mp4 파일을 업로드한다.
  2. Input S3에 mp4 파일이 업로드 되면 Event가 trigger 된다.
  3. 미리 만들어 놓은 Lambda function이 실행 되면서 Media Convert에 Job을 생성한다.
  4. Media Convert로 HLS 로 변환하여 output S3에 저장한다.
  5. Output S3에 Cloud Front를 연결한다.
  6. Streaming이 잘 되는지 확인 해 본다.

범위

참고: VOD Streaming Server 구축 1 (AWS)
참고: VOD Streaming Server 구축 2 (AWS)
참고: VOD Streaming Server 구축 3 (AWS)

  1. Lambda function 생성 및 코드 작성
  2. Lambda function으로 Job 생성 및 MediaConvert 에서 mp4 -> HLS 변환
  3. Output S3에 HLS로 변환된 파일들 저장
  4. Akamai HLS player를 사용하여 HLS 파일 재생

Lambda Function를 통한 MediaConvert Job 생성

VOD Streaming Server 구축 3 (AWS) 에서 lambda function을 만들고 trigger 및 권한 설정하는 과정을 완료하였다.

지금부터는 lambda function을 수정하여 event를 받아 media convert에 job을 생성할 수 있도록 수정해 보도록 한다.

MediaConvert Job base파일 얻어오기

MediaConvert로 이동한다.

파란 부분을 선택해서 detail page로 들어가게 되면 View JSON버튼을 볼 수 있다.

Copy버튼을 눌러서 전체를 복사한다. 시재로 관심이 있는 부분은 Settings 부분이다.

Settings 안쪽만 남기고 나머지 부분은 모두 삭제한다.

job.json file

{
   "TimecodeConfig":{
      "Source":"ZEROBASED"
   },
   "OutputGroups":[
      {
         "Name":"Apple HLS",
         "Outputs":[
            {
               "ContainerSettings":{
                  "Container":"M3U8",
                  "M3u8Settings":{
                     
                  }
               },
               "VideoDescription":{
                  "Width":720,
                  "Height":480,
                  "CodecSettings":{
                     "Codec":"H_264",
                     "H264Settings":{
                        "MaxBitrate":1500000,
                        "RateControlMode":"QVBR",
                        "SceneChangeDetect":"TRANSITION_DETECTION"
                     }
                  }
               },
               "AudioDescriptions":[
                  {
                     "CodecSettings":{
                        "Codec":"AAC",
                        "AacSettings":{
                           "Bitrate":96000,
                           "CodingMode":"CODING_MODE_2_0",
                           "SampleRate":48000
                        }
                     }
                  }
               ],
               "OutputSettings":{
                  "HlsSettings":{
                     
                  }
               },
               "NameModifier":"720x480_1.5mbps_qvbr"
            },
            {
               "ContainerSettings":{
                  "Container":"M3U8",
                  "M3u8Settings":{
                     
                  }
               },
               "VideoDescription":{
                  "Width":1280,
                  "Height":720,
                  "CodecSettings":{
                     "Codec":"H_264",
                     "H264Settings":{
                        "MaxBitrate":4000000,
                        "RateControlMode":"QVBR",
                        "SceneChangeDetect":"TRANSITION_DETECTION"
                     }
                  }
               },
               "AudioDescriptions":[
                  {
                     "CodecSettings":{
                        "Codec":"AAC",
                        "AacSettings":{
                           "Bitrate":96000,
                           "CodingMode":"CODING_MODE_2_0",
                           "SampleRate":48000
                        }
                     }
                  }
               ],
               "OutputSettings":{
                  "HlsSettings":{
                     
                  }
               },
               "NameModifier":"1280x720_4mbps_qvbr"
            },
            {
               "ContainerSettings":{
                  "Container":"M3U8",
                  "M3u8Settings":{
                     
                  }
               },
               "VideoDescription":{
                  "Width":1920,
                  "Height":1080,
                  "CodecSettings":{
                     "Codec":"H_264",
                     "H264Settings":{
                        "MaxBitrate":8000000,
                        "RateControlMode":"QVBR",
                        "SceneChangeDetect":"TRANSITION_DETECTION"
                     }
                  }
               },
               "AudioDescriptions":[
                  {
                     "CodecSettings":{
                        "Codec":"AAC",
                        "AacSettings":{
                           "Bitrate":96000,
                           "CodingMode":"CODING_MODE_2_0",
                           "SampleRate":48000
                        }
                     }
                  }
               ],
               "OutputSettings":{
                  "HlsSettings":{
                     
                  }
               },
               "NameModifier":"1920x1080_8mbps_qvbr"
            }
         ],
         "OutputGroupSettings":{
            "Type":"HLS_GROUP_SETTINGS",
            "HlsGroupSettings":{
               "SegmentLength":10,
               "Destination":"s3://output/vod/hls/",
               "MinSegmentLength":0
            }
         }
      }
   ],
   "Inputs":[
      {
         "AudioSelectors":{
            "Audio Selector 1":{
               "DefaultSelection":"DEFAULT"
            }
         },
         "VideoSelector":{
            
         },
         "TimecodeSource":"ZEROBASED",
         "FileInput":"s3://input/vod/mp4/Wood Anemones-112429.mp4"
      }
   ]
}

job.json 파일 추가

new file

파일 생성완료.
이제 미리 준비해놓았던 json 파일 내용을 복사해서 붙여넣는다.

저장을 위해서 deploy버튼을 눌러준다.

환경변수 설정

  1. Configuration/Environment variables 선택 (환경변수 선언)
  2. Edit버튼 클릭

  1. Applicaiton: VOD-HLS (Description: 어플리캐이션 용도 - 자유롭게 변경가능)
  2. DestinationBucket: output (Output S3)
  3. MediaConvertRole: arn:aws:iam::12345678:role/role_mediaconvert

혹시 이 권한이 생각나지 않는 사람들을 위해 아래에 설명을 해 놓았다.

VOD Streaming Server 구축 3 (AWS) 에서 role_lambda_vod_execution 만들 때 지정했던 권한

위의 권한을 지정해 주지 않으면 권한 문제로 프로그램이 실행되지 않는다.

Test용 Event 추가


Template를 이용해서 S3에 파일이 추가될 때 발생하는 Event를 미리 선언해서 테스트 해 볼 수 있다.

  1. name: input (Input S3 bucket name)
  2. arn: arn:aws:s3:::input
  3. key: vod/dev/mp4/1/174e7217-bd3e-4aa2-a72b-a24b4eb6e3e4/Anime-84574.mp4

각자 자유롭게 설정 가능

  • vod/dev: 기본폴더위치 (dev를 위한 파일을 의미함)
  • mp4: 파일형식
  • 1: 맴버시퀀스
  • 174e7217-bd3e-4aa2-a72b-a24b4eb6e3e4: UUID - assetID로 활용
  • Anime-84574.mp4: 동영상 파일이름
{
  "Records": [
    {
      "eventVersion": "2.0",
      "eventSource": "aws:s3",
      "awsRegion": "us-east-1",
      "eventTime": "1970-01-01T00:00:00.000Z",
      "eventName": "ObjectCreated:Put",
      "userIdentity": {
        "principalId": "EXAMPLE"
      },
      "requestParameters": {
        "sourceIPAddress": "127.0.0.1"
      },
      "responseElements": {
        "x-amz-request-id": "EXAMPLE123456789",
        "x-amz-id-2": "EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH"
      },
      "s3": {
        "s3SchemaVersion": "1.0",
        "configurationId": "testConfigRule",
        "bucket": {
          "name": "input",
          "ownerIdentity": {
            "principalId": "EXAMPLE"
          },
          "arn": "arn:aws:s3:::input"
        },
        "object": {
          "key": "vod/dev/mp4/1/174e7217-bd3e-4aa2-a72b-a24b4eb6e3e4/Anime-84574.mp4",
          "size": 1024,
          "eTag": "0123456789abcdef0123456789abcdef",
          "sequencer": "0A1B2C3D4E5F678901"
        }
      }
    }
  ]
}

Save버튼 클릭

Source code 추가

Source code의 역할

  1. event 객체에서 input s3 에 올라가 있는 파일의 정보를 받아온다.
  2. MediaConvert에 create job을 실행할 때 ouput s3에 파일 위치를 지정해 준다.
import json
import os
import uuid
import boto3
import urllib.parse

def lambda_handler(event, context):
    print('event: ')
    print(event)
    
    status_code = 200
    body = {'messgae': 'Job 생성이 완료되었습니다.'}
    
    # Source ---------------------------------------------------
    
    # input
    source_s3_bucket = event['Records'][0]['s3']['bucket']['name']
    # vod/dev/mp4/1/174e7217-bd3e-4aa2-a72b-a24b4eb6e3e4/Anime-84574.mp4
    source_s3_key = urllib.parse.unquote_plus(event['Records'][0]['s3']['object']['key'], encoding='utf-8') 
    # s3://input/vod/dev/mp4/1/174e7217-bd3e-4aa2-a72b-a24b4eb6e3e4/Anime-84574.mp4
    source_s3 = 's3://' + source_s3_bucket + '/' + source_s3_key
    # Anime-84574    
    source_s3_base_name = os.path.splitext(os.path.basename(source_s3))[0] # Anime-84574
    
    # Destination ---------------------------------------------------
    # s3://output
    destination_s3 = 's3://' + os.environ['DestinationBucket'] 
    # output
    destination_s3_base_name = os.path.splitext(os.path.basename(destination_s3))[0] 
    # arn:aws:iam::123456:role/service-role/mediaconvert_mp4_to_hls_role
    media_convert_role = os.environ['MediaConvertRole'] 
    # ap-northeast-2 (서울)
    region = os.environ['AWS_DEFAULT_REGION'] 
    
    
    # Job 생성 ---------------------------------------------------
    
    
    job_metadata = {}
    
    splitted = source_s3_key.split('/')
    if len(splitted) == 6:
        base_folder = splitted[0] # vod
        server_type = splitted[1] # dev (qa, prod etc)
        file_type   = splitted[2] # mp4
        member_seq  = splitted[3] # 1
        uuid        = splitted[4] # 174e7217-bd3e-4aa2-a72b-a24b4eb6e3e4
        file_name   = splitted[5] # Anime-84574.mp4
        
        job_metadata['assetID'] = uuid
        job_metadata['baseFolder'] = base_folder
        job_metadata['serverType'] = server_type
        job_metadata['fileType'] = file_type
        job_metadata['memberSeq'] = member_seq
        job_metadata['fileName'] = file_name
        
        # vod/dev/hls/174e7217-bd3e-4aa2-a72b-a24b4eb6e3e4/Anime-84574
        job_metadata['outputPath'] = job_metadata['baseFolder'] + '/' + job_metadata['serverType'] + '/' + 'hls/' + job_metadata['assetID'] + '/' + source_s3_base_name 
        job_metadata['outputExtension'] = '.m3u8'                                     
        
    else:
        status_code = 400
        body['message'] = '지정된 규칙과 일치하지 않습니다. \n vod/서버종류/파일종류/맴버시퀀스/UUID/파일이름 \n ex)vod/dev/mp4/1/174e7217-bd3e-4aa2-a72b-a24b4eb6e3e4/Anime-84574.mp4 '
        
        return {
            'status_code': status_code,
            'headers': {'Content-Type':'application/json', 'Access-Control-Allow-Origin': '*'},
            'body': body
        }

    print('job_metadata: ')
    print(job_metadata)        
    


    try:
        with open('job.json') as file:
            job_settings = json.load(file)
            
        
        mc_client = boto3.client('mediaconvert', region_name=region)
        endpoints = mc_client.describe_endpoints()    
        
        client = boto3.client('mediaconvert', 
                              region_name=region, 
                              endpoint_url=endpoints['Endpoints'][0]['Url'], # https://bnklbqvoa.mediaconvert.ap-northeast-2.amazonaws.com
                              verify=False)
        
        # change input file name
        # s3://input/vod/dev/mp4/1/174e7217-bd3e-4aa2-a72b-a24b4eb6e3e4/Anime-84574.mp4
        job_settings['Inputs'][0]['FileInput'] = source_s3 
        
        # vod/dev/hls/174e7217-bd3e-4aa2-a72b-a24b4eb6e3e4/Anime-84574
        s3_key_hls = job_metadata['outputPath']
        # s3:out/vod/dev/hls/174e7217-bd3e-4aa2-a72b-a24b4eb6e3e4/Anime-84574             
        destination_s3_final = destination_s3 + '/' + s3_key_hls 
        
        # change output path
        job_settings['OutputGroups'][0]['OutputGroupSettings']['HlsGroupSettings']['Destination'] = destination_s3_final 
        
        job = client.create_job(Role=media_convert_role,
                            UserMetadata=job_metadata,
                            Settings=job_settings)
    
    
    except Exception as error:
        print('Exception: ')
        print(error)
    
        status_code = 500
        body['message'] = 'MediaConvert Job 생성중 에러발생'
        raise
        

    finally:
        return {
            'status_code': status_code,
            'headers': {'Content-Type':'application/json', 'Access-Control-Allow-Origin': '*'},
            'body': body
        }

    return {
        'status_code': status_code,
        'headers': {'Content-Type':'application/json', 'Access-Control-Allow-Origin': '*'},
        'body': body
    }      

Test 실행


Test버튼을 눌러서 테스트를 실행한다.

테스트가 완료되는 response를 확인할 수 있다.

MediaConvert 확인

JOB이 잘 생성된 것을 확인할 수 있다.
여기에서 Error가 발생한 이유는 Input파일이 존재하지 않는 경로를 넣어주었기 때문이다.

Input S3 자동 트리거로 인한 MediaConvert 동작확인

Input S3에 폴더 만들기

규칙

  • vod/dev: 기본폴더위치 (dev를 위한 파일을 의미함)
  • mp4: 파일형식
  • 1: 맴버시퀀스
  • 5a17bf91-06e9-11ed-affd-0242ac110002: UUID - assetID로 활용
  • Anime-84574.mp4: 동영상 파일이름

vod/dev/mp4/1/5a17bf91-06e9-11ed-affd-0242ac110002/

나중에 Backend server에서 vod 파일을 올릴 때는 위의 규칙에 맞게 소프트웨어로 생성해 줄 수 있지만 지금은 안되니 이렇게 미리 폴더를 수동으로 만들어 놓았다.

이제 여기에 동영상 파일을 하나 올리자

Seoul - 21985.mp4 라는 파일을 올렸다.

MediaConvert 확인


변환 진행 중

변환 완료

Output S3 파일 확인

의도했던 것처럼 m3u8 형식의 index파일과 일정 시간 단위로 나누어진 ts 파일이 생성된 것을 확인할 수 있다.

Akamai Player를 이용한 Streaming 확인

CloudFront 주소 확인

생성된 CloudFront를 보면 Domain name을 확인할 수 있다.

동영상 플레이 확인

HLS 파일을 Play 하려면 Akamai Player의 도움이 필요하다.

현재 https 관련 설정을 하지 않았기 때문에 akamai player도 http로 접속해야 한다. (http://players.akamai.com/players/hlsjs)

파일 이름 알아내기

파일 이름을 클릭한다.

상세화면 Object URL에서 힌트를 얻을 수 있다.

http://[cloudfront-domain-name]/vod/dev/hls/5a17bf91-06e9-11ed-affd-0242ac110002/Seoul+-+21985.m3u8 을 주소창에 입력하면 결과를 동영상이 다운받아져서 동작하는 것을 확인할 수 있다.

profile
36.9 It's good time to start something new

0개의 댓글