[WIP]S3に置いた動画(.mp4)をLambdaとElasticTranscoderでHLSストリーミング動画(.ts)へ変換してcloudfrontのワンタイムURLで見るよ機構


※動画変換できるわぁい　で作っちゃうと痛い目見るかもしれませんのでご注意を。。

##ざっくり流れ(適当)

![スクリーンショット 2016-04-23 13.41.13.png](https://qiita-image-store.s3.amazonaws.com/0/98253/53728fcf-9d69-408f-9ba5-b6da02419c4f.png "スクリーンショット 2016-04-23 13.41.13.png")

##1. S3
#### 準備

- 適当なS3バケットを作ります　例：hoge001.fuga002.com
- バケット内にmoviesというフォルダを作ります（作らなくても可）
- バケットの設定（後でも可）
	- hoge001.fuga002.comバケットのプロパティ > イベント
		- イベント：Put, CompleteMultiPartUpload  （←アップロードと、分割アップロード）
		- プレフィックス：movies/　（←moviesフォルダ配下）
		- サフィックス：.mp4
		- 送信先：Lambda関数
		- Lambda関数名：mp4_transcoder　（←2で作るLambdaの関数名にします）


##2. Lambda
#### ？Lambdaてなんや？

```
“AWS Lambda は、コードを AWS Lambda にアップロードすると
サービスが AWS インフラストラクチャを使用してコードの実行を代行するコンピューティングサービスです。”
```

#### 手順
1. [Create a Lambda function] します
2. [Skip] します（例文をもとに自分の好きなLambdaを書くのもいいと思います）
3. [Name]はお好きなようにします
4. [Description]もお好きなように説明書きします
5. [Runtime]はNode.jsです
6. Lambda function codeに下記のようなコードを入れます。（一部要編集）

NodeJSコード

```mp4_transcoder
console.log('Loading function');

var aws = require('aws-sdk');
var s3 = new aws.S3({ apiVersion: '2006-03-01' });
var s3tokyo = new aws.S3({apiVersion: '2006-03-01', endpoint: 'https://s3-ap-northeast-1.amazonaws.com'});
var ets = new aws.ElasticTranscoder({apiVersion: '2012-09-25', region: 'ap-northeast-1'});

exports.handler = function(event, context) {
  console.log('Received event:', JSON.stringify(event, null, "  "));
  var bucket = event.Records[0].s3.bucket.name;
  var key = event.Records[0].s3.object.key;
  var uploadFolderName = key.split('/')[0];
  var fileName = key.split('/')[1].split('.')[0];
  var extenSion = key.split('.')[1];
  var filePathAndName = key.split('.')[0];

  s3tokyo.getObject({Bucket:bucket, Key:key}, function(err, data){
    if (err){
    //getObject failed
      console.log(err);
      var message = 'Error getting object ' + key + ' from bucket ' + bucket +
        '. Make sure they exist and your bucket is in the same region as this function.';
      console.log(message);
      context.fail(message);
    }
    else {
    //getObject complete
      console.log('data:', data);
      console.log('bucket:', bucket);
      console.log('key:', key);
      console.log('uploadFolderName:', uploadFolderName);
      console.log('fileName:', fileName);
      console.log('extenSion:', extenSion);
      console.log('filePathAndName:', filePathAndName);
      console.log('ContentType:', data.ContentType);

      var isNotMp4file = (extenSion != 'mp4');
      if (isNotMp4file) {
      //context fail message if it isn't mp4 file.
        console.log('not mp4 file! process.exit!');
        context.fail(message);
        process.exit();
      }
      else {
        //Check the ContentType
        var isNotCorrectContentType = (data.ContentType != 'video/mp4');
        if (isNotCorrectContentType) {
          //if ContentType not equal "video/mp4", export the console.log
          console.log('[must conversion] data.ContentType != video/mp4');
        }
        var type = 'video/mp4';
        var params = {
          Bucket: bucket,
          CopySource: bucket + '/' + key,
          Key: key,
          ContentType: type,
          MetadataDirective: 'REPLACE'
        };
        console.log('PARAMS is :', params);
        console.log('To params set -> video/mp4');

        //Rewrite the ContentType
        s3tokyo.copyObject(params, function(err2, data2){
          if (err2){
            console.log('replace false...');
            context.done('error','error2 getting file '+ String(err2));
          }
          else {
            //Transcode to HLS
            ets.createJob(
                {
                  PipelineId: '*******************',
                  Input:
                  {
                    Key: key,
                    FrameRate: 'auto',
                    Resolution: 'auto',
                    AspectRatio: 'auto',
                    Interlaced: 'auto',
                    Container: 'auto',
                  },
                  Output:
                  {
                    Key: filePathAndName,
                    ThumbnailPattern: filePathAndName + '-thumbs-{count}',
                    SegmentDuration:'60',
                    PresetId: '1351620000001-200050',
                    Rotate: 'auto'
                  }
                }, function(error, data) {
                  if(error)
                  {
                    console.log(error);
                    context.done('Job error...',error);
                  }
                  else {
                    console.log('Job submitted');
                    context.done(null,'');
                  }
                }
            );
            //Transcode to mp3
            ets.createJob(
                {
                  PipelineId: '*******************',
                  Input:
                  {
                    Key: key,
                    FrameRate: 'auto',
                    Resolution: 'auto',
                    AspectRatio: 'auto',
                    Interlaced: 'auto',
                    Container: 'auto',
                  },
                  Output:
                  {
                    Key: filePathAndName + ".mp3",
                    PresetId: '1351620000001-300040',
                  }
                },
                function(error, data)
                {
                  if(error)
                  {
                    console.log(error);
                    context.done('Job error...',error);
                  }
                  else
                  {
                    console.log('Job submitted');
                    context.done(null,'');
                  }
                }
            );
          }
        });
      }
    }
  });
};
```

	上記コードが主に行っていることは
	1. S3にput, CompleteMultiPartUploadされた際にイベント発生
	2. 対象のオブジェクト情報を取得
	3. 拡張子がmp4でない場合はexit
	コンソールログ出力「`not mp4 file! process.exit!`」
	4. content-typeが"video/mp4"でない場合はexitコンソールログ出力「`[must conversion] data.ContentType != video/mp4`」
	5. それ以外であればElastic TranscoderへJOB出力

6.Roleは、管理者権限があればlambda_basic_executionというRoleをその場で作り、後ほどにでもIAMの当該Roleに対して、S3へのアクセスポリシーと、ElasticTranscoderへのアクセスポリシーを付与します。（重要）
7.メモリやタイムアウト値は大きめがよいです。ただし…その分…お金が…
8.VPCは任意といった感じでしょうか..？(最近追加されたので詳しくは知らず。。)


####その他備考
- console.logはCloudWatchへ出力されるデバッグ用です。ログの出力先は AWS CloudWatch > ロググループ /aws/lambda/mp4_transcoder 
- フォルダ構成を深く掘る場合は、splitをうまく使ってください
- PipelineId: '**********'にはElastic Transcoderで作成したパイプラインIDを入力します
- Elastic Transcoderのジョブ作成の各パラメータ（.tsとか.flvとか.mp3に変換する際の）は下記を参考にしました
http://docs.aws.amazon.com/ja_jp/elastictranscoder/latest/developerguide/create-job.html

- 料金：https://aws.amazon.com/jp/lambda/pricing/
0.20 USD/1,000,000 件のリクエスト（0.0000002 USD/秒）

##3. Elastic Transcoder
#### ？Elastic Transcoderてなんや？

```
“Amazon Elastic Transcoder はクラウドのメディア変換サービスです。
高度なスケーラビリティ、使いやすさ、高い費用効率性を実現する設計で、開発者や企業は、メディアファイルをその元のソース形式から
スマートフォン、タブレット、PC などのデバイスで再生可能できるバージョンに変換（または「トランスコード」）できます。”
```

#### パイプライン（←変換元と先の道路的な）のおもなの設定
- 概要
	- Name：　mp4-transcoder-pipeline
	- Status：　Active
	- Input Bucket：　hoge001.fuga002.com
- トランスコード先、サムネイル保存先
	- Bucket：　hoge001.fuga002.com
	- Storage Class：　Standard
- IAM Role
	- Elastic_Transcoder_Default_Role　（←先ほどと同様にその場で作成）
- Notifications
	- On Completion Event: Create a New SNS Topicから成功時のトピック名を適当に作成しておきます。
	- On Error Event： 同様にCreate a New SNS Topicから失敗時のトピック名を適当に作成しておきます。（後述するAWS SNSへ通知が飛ぶようになります。）

####動き
- LambdaがpipelineIDを指定し、Jobを作成する。　（jobは各動画ファイルの変換命令書）
- Jobsのページで、pipelineIDを指定し、[search]を押すとエンコード履歴が確認できます。
- JobのStatusは確認したものは以下
	- 成功：Complete
	- 実行中：Proccessing
	- 失敗：Error
	- 待機中：Submitted
- Errorの場合は、JobのIDの左側にある虫眼鏡のアイコンをクリックし、"Status Details"のエラー内容を確認すること。

- 料金:https://aws.amazon.com/jp/elastictranscoder/pricing/

##4. SNS（Simple Notification Service） 
#### ？Simple Notification Serviceてなんや？

```
“Amazon SNS は、高速で、柔軟性に優れ、完全マネージド型の pub-sub メッセージングサービスです。
Amazon SNS は、通知、メール、SMS メッセージをプッシュするクラウドベースのモバイルアプリケーション通知サービスとして、
またはエンタープライズメッセージングインフラストラクチャとして使用できます。”
```

1. Amazon SNSのページの、左メニュー"Topics"から、先ほどElastic Transcoderで設定したTopicを表示します。
2. CompleteかErrorどちらかのトピックのARN名をクリックします。
3. Create Subscriptionを押下し、好きなProtocolとEndpointを指定すれば、そのEndpoint宛へ購読可否が飛びます。
（例：
Protocol → Email
Endpoint → 自分のメールアドレス）

4. 届いたメール内のConfirm subscriptionのリンクへ飛べば購読手続き完了です。

##5. CloudFront
#### ？CloudFrontてなんや？

```
“Amazon CloudFront はコンテンツ配信ウェブサービスです。
他のアマゾン ウェブ サービス製品と統合されたこのサービスを利用すると、開発者や事業主は、
低レイテンシーと高速なデータ転送速度で簡単にエンドユーザーにコンテンツを配信できます。最低利用量の条件はありません。”
```

- 料金:https://aws.amazon.com/jp/cloudfront/pricing/

####おもな設定
coming soon...


##所感
そのうちcloudfrontの連携内容も書きます。

##参考にしましたURL様
- http://qiita.com/ricken360/items/3223630da37956a1b96c
- http://akiyoko.hatenablog.jp/entry/2015/08/09/232933
- http://qiita.com/con_mame/items/b58a5e017e7c9ab2356b#%E3%82%A8%E3%83%B3%E3%82%B3%E3%83%BC%E3%83%89%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B
- http://qiita.com/terapyon/items/ff2dd73bd479fbd44e82
- http://qiita.com/terapyon/items/b77ac981da790662e247
- http://popkirby.github.io/contents/nodeguide/style.html#conditions
- http://docs.aws.amazon.com/ja_jp/elastictranscoder/latest/developerguide/create-job.html#create-job-requests
- http://dev.classmethod.jp/cloud/aws/iam-policy-for-project-members/