ã¯ããã« ã¯ãããŸããŠãã¡ãã¬ãŒæ°åå
¥ç€Ÿ 2 幎ç®ã®æ£®å·ã§ãã ã€ã³ãã©çµéšããŸã 4 ã¶æã»ã©ã®æªçè
ã§ããã AWS èªå®è³æ Œã¯ã©ãŠããã©ã¯ãã£ã·ã§ã㌠ã®è©Šéšã«åæ Œããããšãã§ããŸãããäžäœã®è³æ ŒååŸã«åããŠä»åŸãå匷ããŠãããŸãã å
æ¥ç§ãæ
åœãããŠããã ãã CloudFront ã®ã¢ã©ãŒãæ¹åã«ã€ããŠãåé¡ã®åå ãšå¯Ÿå¿æ¹æ³ãæ¬èšäºã§æžãããŠããã ããŸãã ãããããã°ãä»ãåããã ããã èæ¯ãšåé¡ åŒç€Ÿãéå¶ããŠãããããã¯ãã®äžã€ ãžã§ãã¡ãã¬ãŒ ã§ã¯ã€ã³ãã©ç°å¢ã« AWS ãå©çšããŠããŸãã ç£èŠã«ã¯ CloudWatch ã Datadog ãªã©ã䜿çšããŠããŸãããµãŒãã¹ã®ç°åžžãæ€ç¥ããããã®èšå®ã®ã²ãšã€ã«ãCloudFront ã®ãšã©ãŒã¬ã¹ãã³ã¹å¢å ãæ€ç¥ããããã®ã¢ã©ãŒãéç¥ããããŸãã CloudFront ãè¿ãã¬ã¹ãã³ã¹ã®ãã¡ãç¹å®ã®æéç¯å²ã®äžã§ 4xx, 5xx ç³»ã®ãšã©ãŒãè¿ããå²åãéŸå€ãè¶
éããããšãæ€ç¥ããŠãCloudWatch ã¢ã©ãŒã ãã Lambda ãéã㊠Slack ã«éç¥ãè¡ã£ãŠããŸãã ãšããããããé ãå¢ã« CloudFront ã§ã® 4xx ç³»ãšã©ãŒã¬ã¹ãã³ã¹ã®çºçå²åãå¢å ããã¢ã©ãŒãã®éç¥é »åºŠãæ³å®ä»¥äžã«é«ããªã£ãŠããŸããŸããã åå 調æ»ãè¡ã£ããšãããå·æ°ãã瀟å
ã·ã¹ãã ã«ãŠä»¥äž 2 ã€ã®åå ã§ã¢ã©ãŒããçºçããŠããããšãåãããŸããã åå 1. 瀟å€ãµãŒãã¹ããã®ã¢ã¯ã»ã¹ã§ã¢ã©ãŒããçºç CloudFront ã®ãã°ã確èªãããšããã瀟å€ãµãŒãã¹ïŒSlack, Google ã¹ãã¬ããã·ãŒããªã©ïŒããã®ã¢ã¯ã»ã¹ã«å¯ŸããŠã¹ããŒã¿ã¹ã³ãŒã 403 ãè¿ããŠããã¬ã¹ãã³ã¹ãã°ãæ°å€ãèšé²ãããŠããŸããã ãããã®ãµãŒãã¹ã«åŒç€Ÿã®ç€Ÿå
管çã·ã¹ãã ã® URL ããã¹ãããããšããã¬ãã¥ãŒã衚瀺ããããã®ãªã¯ãšã¹ããéä¿¡ãããŸããããã®æã®ãªã¯ãšã¹ãã瀟å€ããã®ã¢ã¯ã»ã¹ãšã㊠WAF ã§å¶éãããŠããŸããã ã€ã³ãã©å·æ°åããçŸåšãŸã§çšŒåããŠãã CloudFront ã®ãã°ã確èªãããšããããã¡ãã§ãåæ§ã®ãšã©ãŒã¬ã¹ãã³ã¹ãçºçããŠããããšãåãããŸããããããããšã©ãŒå²åå¢å ã®ã¢ã©ãŒããé »çºããããšã¯çŸåšã§ãã»ãšãã©ãããŸããã 以åã¯ãžã§ãã¡ãã¬ãŒãæã€ã·ã¹ãã å
šäœãžã®ã¢ã¯ã»ã¹ãã²ãšã€ã® CloudFront ã§åŠçããŠãããããã¢ã©ãŒãéç¥ã®å²åãšããŠèšç®ããéã®æ¯æ°ã倧ããã瀟å€ããã®ã¢ã¯ã»ã¹ã«ãããšã©ãŒãçºçããŠããŠãããã®å²åãéŸå€ãè¶
éããããšãå°ãªãã£ãããã ãšèããããŸãã ã€ã³ãã©æ§æãå·æ°ããããšããã£ããã«ããããŸã§ç®ç«ã£ãŠããªãã£ã瀟å€ããã®ã¢ã¯ã»ã¹ãšããåé¡ã衚é¢åããŠããã®ã§ãã åå 2. å©çšè
ãå°ãªãæéã«ãšã©ãŒã¬ãŒããé«ããªãã¢ã©ãŒããçºç CloudWatch ã¢ã©ãŒã ã§ã¯ãäžå®æéå
ã§ã®ã¬ã¹ãã³ã¹ã®ãã¡ã4xx, 5xx ç³»ã®ãšã©ãŒããšã«ãã®å²åãéŸå€ãè¶
éããããšãæ€ç¥ããŠã¢ã©ãŒããçºçãããèšå®ãšããŠããŸããã ããããæ·±å€ãªã©å©çšè
ãå°ãªãæéã«äžåºŠã§ããšã©ãŒãçºçãããšããã®å²åãè·³ãäžãã£ãŠããŸãããšã§ã¢ã©ãŒãçºçé »åºŠãå¢å ãã誀æ€ç¥ãšèšããç¶æ
ã«ãªã£ãŠããŸããã 以äžã®ç»åã§ã¯ã4xx ç³»ãšã©ãŒã®å²åãå€éã« 100%ãšãªã£ãŠããç®æã確èªã§ããŸããïŒè¡šç€ºæé㯠UTC ã§ãïŒ å¯Ÿå¿æ¹æ³ 2 ã€ã®åå ã«å¯Ÿãããããã察å¿ãè¡ããŸããã å¯Ÿå¿ 1. ç¹å®ã®ç€Ÿå€ãµãŒãã¹ããã®ã¢ã¯ã»ã¹ããšã©ãŒæ€ç¥ã®å¯Ÿè±¡å€ãšãã åãµãŒãã¹ã®èšå®ã«ããããã¬ãã¥ãŒè¡šç€ºã«ããã¢ã¯ã»ã¹ã忢ãããéžæè¢ãèããããŸãããããã該åœãããµãŒãã¹ãã¹ãŠã«èšå®ãè¡ãã®ã¯é£ããã管çãè€éã«ãªãããã§ãã ããã§ãç¹å®ã®ç€Ÿå€ãµãŒãã¹ããã®ã¢ã¯ã»ã¹ã ãšã©ãŒæ€ç¥ã®å¯Ÿè±¡å€ãšãã æ¹éã§å¯Ÿå¿ãè¡ããŸããã ãã°ã®ãã¹ãŠã CloudWatch ã¢ã©ãŒã ã®è©äŸ¡å¯Ÿè±¡ãšããŠããããã«ã誀æ€ç¥ãšèšããã¢ã©ãŒããçºçããŠããã®ãçŸç¶ã§ãããããã£ãŠãè©äŸ¡ãããããã°ã ãã«çµã CloudWatch ã§è©äŸ¡ãããããšãã§ããã°è§£æ±ºãå³ããŸããä»åã§ããã°ãç¹å®ã®ãŠãŒã¶ãŒãšãŒãžã§ã³ãã IP ã¢ãã¬ã¹ãªã©ãé€å€ã㊠CloudWatch ã«æž¡ããšããåŠçãæ±ããããŸãã ãã®å®çŸã®ãããä»åæ°ãã«äœæããã®ã Lambda ã®é¢æ°ã§ãã S3 ã« CloudFront ã®ãã°ãä¿åãããããšãããªã¬ãŒã« Lambda ãèµ·åãããããã«èšå®ããŸããã ãã°ããšã«èšé²ãããŠãããªã¯ãšã¹ãå
ã®ãŠãŒã¶ãŒãšãŒãžã§ã³ãã IP ã¢ãã¬ã¹ãªã©ã確èªããé€å€å¯Ÿè±¡ãã©ãããå€å®ããŸãã ããããŠéžå¥ãééãããã°ãä»åºŠã¯ã¹ããŒã¿ã¹ã³ãŒãã® 5 ã€ã®ã¯ã©ã¹ïŒ1xx, 2xx, 3xx, 4xx, 5xx ç³»ïŒããšã«æ¯ãåããŸãã ãã ããCloudFront ã§ã¯ã¹ããŒã¿ã¹ã³ãŒãã« 000 ãå
¥ãããšããããŸãã https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/AccessLogs.html ã¹ããŒã¿ã¹ã³ãŒã 000 ã¯ã¢ã©ãŒãã§æ€ç¥ãããšããã§å¯Ÿå¿ã§ããããšãç¹ã«ãªããããæ€ç¥å¯Ÿè±¡ããé€å€ããæ¹éãšããŸããã ïŒS3 ã®ãã°ãçŽæ¥ç¢ºèªãããš 000 ãªã®ã§ãããAthena ã§ãã°ã確èªãããš 0 ã§è¡šç€ºããããããå°ãããããŸããïŒ ãããã£ãæå³ããªãå€ãã¹ããŒã¿ã¹ã³ãŒãã«å«ãŸããŠããå Žåãªã©ãæ€ç¥ã§ããããã«ããããã5 ã€ã®ã¯ã©ã¹ä»¥å€ã®å€ãå«ãŸããŠããå Žåã« UNKNOWN_STATUS_CODE ãªã¯ã©ã¹ãšããŠåé¡ããããã«ããŸããã å¿
èŠãªãã®ã«çµã£ããã°ã 6 ã€ã®ã¹ããŒã¿ã¹ãã¿ãŒã³ã«åããããããã®ä»¶æ°ã CloudWatch ã¡ããªã¯ã¹ãž PUT ãããŸãã ãããŸã§ã Lambda ã®ä»äºãšãªããŸãã åã¹ããŒã¿ã¹ã®ãã°ã®ä»¶æ°ã CloudWatch ã¡ããªã¯ã¹ã§ç¢ºèªã§ããããã«ãªã£ãã®ã§ãã¬ã¹ãã³ã¹å
šäœã«ããã 4xx, 5xx ç³»ãšã©ãŒã®å²åãç®åºã§ããŸãããããå
ã«éŸå€ãèšå®ãã以åã®ãããªã¢ã©ãŒããäœæããããšãã§ããŸããã å¯Ÿå¿ 2. CloudWatch ã¢ã©ãŒã ã®æ€ç¥ã«ãŒã«ã調æŽãã å©çšè
ãå°ãªãæéã«ãšã©ãŒã¬ãŒããé«ããªãã¢ã©ãŒããçºçããä»¶ã«ã€ããŠã¯ã CloudWatch ã¢ã©ãŒã ã®æ€ç¥ã«ãŒã«ãèª¿æŽ ããããšã«ãã£ãŠå¯Ÿå¿ããŸããã äžå®æéã§ã®ãšã©ãŒæ°ã«éŸå€ãå®ããè¶
éããéã«ã¢ã©ãŒããéç¥ããããã«å€æŽããŸãããã€ãŸããå²åã§ã¯ãªã絶察æ°ã§å€æãããããã«ããŠããŸãã 以äžã®ç»åã®ç·è²ã®ã°ã©ããæ°ããªæ€ç¥ã«ãŒã«ã§åç
§ãããã®ãšãªããŸããæ©è²ã§ç€ºããŠããã®ã 4xx ç³»ãšã©ãŒã®å²åã§ãããããã 100%ãšãªã£ãŠããç®æã«ãããŠãæ°ããªæ€ç¥ã«ãŒã«ã«ã¯åå¿ããŠããªãããšãåãããŸãã 察å¿ãçµã㊠Lambda ãçšããéèšåŠçã®äœæãšãã¢ã©ãŒãã®æ€ç¥ã«ãŒã«ã®èª¿æŽãè¡ãããšã§ãCloudFront ã®ãšã©ãŒç£èŠç²ŸåºŠãåäžãããããšãã§ããŸããã 以åã¯é »ç¹ã«ã¢ã©ãŒããããã£ãŠããŸãããã察å¿åŸã¯ãã£ããèœã¡çããèŠããŠããŸãã ã·ã¹ãã ã®å®å®çšŒåãå®çŸããããã«ããé©åã«ã¢ã©ãŒããæ€ç¥ã§ããããã«ä»åŸãæ¹åãå³ã£ãŠãããããšæããŸãã ä»åã®èª²é¡ã«å¯Ÿããè§£æ±ºææ®µãšããŠã¯ã·ã³ãã«ãªå¯Ÿå¿ã§ãã£ãããšã¯æããŸãããç§ã«ã¯å®ãã®å€ãçŽäœæ²æãªçµéšãšãªããŸããã AWS ã®åºæ¬çãªãµãŒãã¹ã®é£æºãåŠã¶ããšãã§ããããšã«å ããæ°ãã«äœæãã AWS ã®ãµãŒãã¹ã®èª²éé¡ã®è©Šç®ããå®è¡èšç»ãå®ããŠããã®å®è£
ãªã©äºåæºåãæèããŠåãçµãããšãã§ããŸãããæµãŸããç°å¢ã®äžãæ¥ã
åŠã°ããŠããã ããŠãããŸãã ãããã« ã¡ãã¬ãŒã§ã¯ãå»çãã«ã¹ã±ã¢ã®æªæ¥ãã€ããããšããããã·ã§ã³ãæ²ããåãããã¯ãã®éçºã»éå¶ãé²ããããŠããŸãã ãšã³ãžãã¢ã»ãã¶ã€ããŒãã¯ããå€ãã®ããžã·ã§ã³ã§æ°ããªã¡ã³ããŒãåéããŠããŸãããèå³ããæã¡ããã ããæ¹ã¯ããã²ãæ°è»œã«ã話ããããŠããã ããã°ãšæããŸãïŒ ãããŸã§ãä»ãåãããã ããããããšãããããŸããã åéã®äžèЧ | æ ªåŒäŒç€Ÿã¡ãã¬ãŒ ã¡ãã¬ãŒã®æ¡çšæ
å ±ã¯ãã¡ãããã確èªãã ããã www.medley.jp
ããã«ã¡ã¯ã第äžéçºã°ã«ãŒãã®ç¢éã§ãããžã§ãã¡ãã¬ãŒéçºãšã³ãžãã¢ãšããŠãäž»ã«ããã¯ãšã³ããæ
åœããŠããŸãã çŽè¿ã§ã¯ããžã§ãã¡ãã¬ãŒãå
æãªãªãŒã¹ãã ãåç»éžèã æ©èœã®éçºãããžã§ã¯ãã«æºãã£ãŠãããåç»ãã¡ã€ã«ã®ã¢ããããŒãïŒé
ä¿¡ç°å¢ã®èšèšã»å®è£
ãè¡ã£ãŠããŸããã ä»åã®ããã°ã§ã¯ããã®ãåç»éžèãæ©èœã®éçºã«å©çšãã AWS Elemental MediaConvert ãµãŒãã¹ãšã hls.js ãšãã OSS ã©ã€ãã©ãªã«ã€ããŠç޹ä»ããããšæããŸãã ãžã§ãã¡ãã¬ãŒã®ãåç»éžèãæ©èœ ã¯ããã«ãä»åãªãªãŒã¹ãããåç»éžèãæ©èœã«ã€ããŠæŠèŠã玹ä»ããŸãã æ°åã³ãããŠã€ã«ã¹æææ¡å€§ã«ãã£ãŠã察é¢ã§ã®é¢æ¥ã«äžå®ãæããããå
Œ
±äº€éæ©é¢ã®å©çšãé£ãããªã£ããããããšã«ãããæºè¶³ãªè»¢è·æŽ»åãã§ããªããªã£ãŠããæ¹ãããã£ãããããšæããŸãã ãã®ãããªèª²é¡ã解決ããããã«ããžã§ãã¡ãã¬ãŒã§ã¯ãªã¢ã«ã¿ã€ã ã«ãªã³ã©ã€ã³ã§é¢æ¥ãè¡ããWEB 颿¥ããšãäºæ¥è
ããããããèšå®ãã質åã«å¯ŸããŠå¿åè
ãåç»ã§åçãéããåç»éžèãã® 2 ã€ã®æ©èœãæäŸéå§ããããŸããã ref. WEB 颿¥ã»åç»éžèæ©èœã®ãªãªãŒã¹ã®ãç¥ãã åç»éžèïŒåç»é¢æ¥ïŒã¯ãè¿å¹Žå¢å åŸåã«ãããªã³ã©ã€ã³éžèã®äžçš®ã§ããäžè¬çã«ãæ±è·è
/ 就掻çã PC ã»ã¹ããŒããã©ã³çã®ã«ã¡ã©ã§ãäºãçšæãããèšåã«å¿ããŠåç»ãæ®åœ±ããäŒæ¥ã«éãããšã§éžèãè¡ããŸãã ref. WEB 颿¥ã»åç»éžèãšã¯ïŒ 宿œã®æµãã䜿çšããŒã«ããããŒã泚æç¹ãªã©ã培åºè§£èª¬ïŒ ç§ãã¡ãžã§ãã¡ãã¬ãŒã®åç»éžèã§ã¯ãäºæ¥æããããããèšå®ãã質åã«å¯ŸããŠãæ±è·è
ãåçåç»ãæåºããããšãã§ããŸããäºæ¥æãæ±è·è
ããåç»ã§è³ªåã»åçãéãããšã§ãæžé¡ã ãã§ã¯äŒãããªãé°å²æ°ã匷ã¿ãçžæã«äŒããããšãã§ããŸãã WEB 颿¥ã»åç»éžèæ©èœã®ãªãªãŒã¹ã®ãç¥ãã åç»é
ä¿¡ãµãŒãã¹ã®èšèšãã€ã³ã Web ã¢ããªã§ãã®ãããªåç»é
ä¿¡ãµãŒãã¹ãéçºããå ŽåãããŠãŒã¶ã«ããåç»ã¢ããããŒãç°å¢ããšããŠãŒã¶ãžã®åç»ã®é
ä¿¡ã»åçç°å¢ããæäŸããå¿
èŠããããŸãã ãžã§ãã¡ãã¬ãŒã§æ±ãåç»ã¯äžè¬å
¬éããããã®ã§ã¯ãªããå
¬éæ¡ä»¶ãè€éã§ãã ãã£ãŠä»åã¯ããã®ãåç»ã¢ããããŒãïŒé
ä¿¡ç°å¢ããèªãµãŒãã¹å
ã«æ§ç¯ããæ¹éããšãã以äžã®ãããªåç»ãŸããã®èšèšãã€ã³ãã«ã€ããŠæ€èšã»æè¡éžå®ãè¡ãããšã«ããŸããã ïŒãã¡ãããèŠä»¶ã«ãã£ãŠã¯ YouTube ããæ³äººåãåç»é
ä¿¡ãã©ãããã©ãŒã ãå¥çŽããæ¹ãæè»œãªå ŽåãããããšæããŸãïŒ åç»ã®é²ç»ã»æ®åœ± ãµããŒããããåç»ãã¡ã€ã«ã®ãã©ãŒããããã©ãããã Web ã¢ããªå
ã«é²ç»æ©èœãèšããã åç»ã®ã¢ããããŒãïŒã¹ãã¬ãŒãžïŒ åç»ãã¡ã€ã«ã®ããªããŒã·ã§ã³ã§ãåç»ãã¡ã€ã«ã®è§£æããè¡ãã åç»ãã¡ã€ã«ã®ã¢ããããŒãå
ïŒã¹ãã¬ãŒãžïŒãã©ãã«ããã åç»ã®ãšã³ã³ãŒã åç»ãã¡ã€ã«ã®ãšã³ã³ãŒã圢åŒïŒH.264ãHLS çïŒãã©ãããã éåæãšã³ã³ãŒãã®å Žåãã¹ããŒã¿ã¹æ€ç¥ã»ãšã©ãŒãã³ããªã³ã°ãã©ãããã åç»ã®é
ä¿¡ïŒããŠã³ããŒãïŒ é
信圢åŒïŒããŠã³ããŒãïŒã¹ããªãŒãã³ã°ïŒãã©ãããã æå·åãããå Žåã埩å·ãã©ã®ããã«è¡ãã åç»ãã¡ã€ã«ã®å
¬éæ¹æ³ïŒã¢ã¯ã»ã¹å¶éïŒãã©ãããã åç»ã®åç Web ããŒãžäžã§åçãããã®ãããã®å Žåã®è¡šç€ºã»åçå¶åŸ¡ãã©ãããã ãã©ãŠã¶ãµããŒããã©ããŸã§ã«ããããé察å¿ã»ãšã©ãŒæã®å¶åŸ¡ãã©ãããã ä»åã¯ãäžèšã®å€ªåã§èšèŒãã ãåç»ã®ãšã³ã³ãŒããã« MediaConvert ãã ãåç»ã®åçãã« hls.js ãããããæ¡çšããŠããŸãã åé
ã®è©³çްã¯çããŸãããå
šäœãéããŠå€§ãŸãã«ã以äžã®ãããŒã§ãåç»ã¢ããããŒã â ãšã³ã³ãŒãïŒå€æïŒâ é
ä¿¡ã»åçããå®çŸããããšã«ããŸããã ãã©ãŠã¶ãã Ajax ã§åç»ã S3 ãžã¢ããããŒããã MediaConvert ãåç»ã HLS 圢åŒã«ãšã³ã³ãŒãïŒå€æïŒãã ãã©ãŠã¶ã§ hls.js ã䜿ãåç»ã CloudFront ããã¹ããªãŒãã³ã°åœ¢åŒã§åä¿¡ãåçãã ä»åã¯ãã®ãåç»ã¢ããããŒã â ãšã³ã³ãŒãïŒå€æïŒâ é
ä¿¡ã»åçãã«çŠç¹ãçµããMediaConvert ãš hls.js ãã©ã®ããã«äœ¿ã£ãã®ãã玹ä»ããŸãã MediaConvert ã«ãã HLS ãšã³ã³ãŒã AWS Elemental MediaConvert ã¯ãS3 ãšã®èŠªåæ§ãé«ããã¡ã€ã«ããŒã¹ã®åç»å€æãµãŒãã¹ã§ããèªåã§ ffmpeg ãªã©ã䜿ã£ãŠåç»ãšã³ã³ãŒããµãŒããæ§ç¯ã»ç®¡çããããšãªããã¹ã±ãŒã©ãã«ãªåç»å€æåŠçãæè»œã«ã·ã¹ãã ã«çµã¿èŸŒãããšãã§ããŸãã ref. AWS Elemental MediaConvert æéã¯åºåããåç»ã®åçæéã«å¿ããåŸé課éã§ããAWS ã³ã³ãœãŒã«ãã GUI ããŒã¹ã§ãšã³ã³ãŒãèšå®ãäœæãããããžã§ãïŒãšã³ã³ãŒãåŠçïŒãç»é²ããããšãã§ããŸãã ãŸããä» AWS ãµãŒãã¹åæ§ã« API ãæäŸãããŠãããAWS CLI ãåèšèªã® SDK ã䜿ã£ãŠããã°ã©ã ãããšã³ã³ãŒãåŠçãç»é²ããããšãã§ããã·ã¹ãã 飿ºã容æã§ãã # CLI ã§ãšã³ã³ãŒããžã§ããç»é²ããäŸ $ aws --endpoint-url https://abcd1234.mediaconvert.region-name-1.amazonaws.com --region region-name-1 mediaconvert create-job --cli-input-json file://~/job.json äžèš CLI ã³ãã³ãã§äžã®ãããªãšã³ã³ãŒãèšå®ãèšèŒãã JSON ã䜿ããžã§ããäœæãããšãS3 äžã®åç»ãã¡ã€ã«ããµã¯ããšãšã³ã³ãŒãããŠãããŸãããžã§ãã¯ãã¥ãŒã€ã³ã°ãããå
éšã§äžŠååŠçãããããã倧éã®ãšã³ã³ãŒãèŠæ±ã«ãç°¡åã«å¿ããããšãã§ããŸãã { ... "Settings" : { "Inputs" : [ { # å
¥åå
ã® S3 ãã±ããäžã®åç»ãã¡ã€ã« key ãæå® "FileInput" : "s3://testcontent/720/example_input_720p.mov" } ], "OutputGroups" : [ { "OutputGroupSettings" : { "FileGroupSettings" : { # åºåå
ã® S3 ãã±ãã key ãæå® "Destination" : "s3://testbucket/output" } }, # åç»ã»é³å£°ã®ãšã³ã³ãŒãèšå®ãæå® # ããã§å質ã¬ãã«æ¯ã«æ¯ãåããè€æ°ã®ãã¡ã€ã«ãåºåããã # ãµã ãã€ã« jpg ãäœæãããããããšãå¯èœ "Outputs" : [ { "VideoDescription" : { ⊠}, "AudioDescriptions" : { ⊠} } ] } ] } } ref. AWSCLI ã䜿çšãã AWSElemental MediaConvertCreateJob ã®äŸ ãšã³ã³ãŒããå®äºãããžã§ãã¯ãcron + SDK ãªã©ã§ API ãä»ããŠå®æãã§ãã¯ããä»ã«ãCloudWatch Events ã«ããã€ãã³ãç£èŠ â Lambda ã§åŠçãããããªããšãã§ããŸãã ref. AWS Elemental MediaConvert ã«ãã CloudWatch ã€ãã³ã ã®äœ¿çš ãªãåç»ãåãšã³ã³ãŒãããã®ã éåžžããŠãŒã¶ããã¢ããããŒããããåç»ãã¡ã€ã«ã¯ãæ¢ã«äœããã®ã³ãŒããã¯ã§å§çž®ãã .mp4 ã .mov ãªã©ã®ã³ã³ãããã©ãŒãããã«å€æãããŠããããšãæ®ã©ã§ãã ããã Web ããŒãžã§ <video> ã¿ã°ã䜿ããããåç»ãã¡ã€ã«ãåçããããšããå Žåã ãåç»ãã©ãŒãããã«ãã©ãŠã¶ãé察å¿ã ãšåçã§ããªãã ãšããç°å¢äŸååé¡ããããŸãã ãã©ãŠã¶ãšåç»ãã©ãŒãããã®ãµããŒã衚 ref. HTML5 video > Browser support ãã®åé¡ã«å¯Ÿå¿ãããããå€ãã®åç»é
ä¿¡ãµãŒãã¹ã§ã¯ããŠãŒã¶ã®åç»ãå€ãã®ç°å¢ã§åçå¯èœãª MP4 ã³ã³ãããã©ãŒãããïŒH.264 + AAC ã³ãŒããã¯ïŒãªã©ã®åœ¢åŒãžãåãšã³ã³ãŒããããŠããŸãã ãžã§ãã¡ãã¬ãŒã®åç»éžèã§ã¯äžèšç®çã«å ããŠãåç»é²èЧæã®åç·ã»ç«¯æ«è² è·ãæãã ãHTTP ã¹ããªãŒãã³ã°åœ¢åŒã ã§åç»ãé
ä¿¡ããããã«ãã¢ããããŒããããåç»ãå
šãŠ HLS åœ¢åŒ ã«ãšã³ã³ãŒãããŠããŸãã HLS - HTTP Live Streaming åœ¢åŒ HLS 㯠HTTP Live Streaming ã®ç¥ã§ãApple 瀟ã®éçºããèŠæ Œã§ããHTTP ããŒã¹ã®ã¹ããªãŒãã³ã°éä¿¡ãããã³ã«ã§ã现åãã«ãã MP4 åç»ãã¡ã€ã«ãåå²ããŠã³ããŒããããããšã§åç»ã®ã¹ããªãŒãã³ã°é
ä¿¡ãå®çŸããŠããŸãã HLS 圢åŒã«ãšã³ã³ãŒããããåç»ã¯ .ts ãšããåå²ãããã¡ãã£ã¢ãã¡ã€ã«çŸ€ãšã .m3u8 ãšãããã¡ãã£ã¢ãã¡ã€ã«ã®ååŸå
ãç§æ°ãªã©ãèšèŒããããã¹ããã¡ã€ã«ã§æ§æãããŸãã .m3u8 ãã¡ã€ã«ã®äŸïŒãããã§ã¹ããã¡ã€ã«ããã¬ã€ãªã¹ããã¡ã€ã«ãšãïŒ #EXTM3U #EXT-X-TARGETDURATION:10 #EXT-X-VERSION:3 #EXT-X-MEDIA-SEQUENCE:0 #EXT-X-PLAYLIST-TYPE:VOD #EXTINF:9.97663, media-0.ts #EXTINF:9.97663, media-1.ts #EXTINF:7.10710, media-2.ts #EXT-X-ENDLIST ref. RFC 8216: HTTP Live Streaming HLS ã¯ä»ã®ã¹ããªãŒãã³ã°åœ¢åŒãšæ¯èŒããŠãã©ã€ãé
ä¿¡ / VOD ã©ã¡ãã«ã察å¿å¯èœãªããšã察å¿ãã©ãŠã¶ãå€ãããšãå°çšã®é
ä¿¡ãµãŒãã䜿ããã«é
ä¿¡å¯èœãªããšãªã©ãããè¿å¹Žã®åç»é
ä¿¡ãµãŒãã¹ã§åºãå©çšãããŠããŸãã Web ãšã³ãžãã¢ã®èŠç¹ããèŠãŠãã HTTP ããŒã¹ãªãããã£ãã·ã¥ã HTTPS æå·åãªã©ãæ¢å Web æè¡ãšæãåãããããšãæ³åãããããæ±ããããå°è±¡ã§ããã MediaConvert ã® HLS ãšã³ã³ãŒããžã§ãèšå® å®éã«ããã°ã©ã ãã API çµç±ã§ HLS ãšã³ã³ãŒããžã§ããç»é²ããéã®èšå® JSON ã¯ã以äžã®ããã« GUI ã§ãžã§ããã³ãã¬ãŒããäœæããŠç¢ºèªããããšãã§ããŸãã ãã®ã JSON ã衚瀺ãã§ãåè¿°ãã CLI ã³ãã³ã mediaconvert create-job --cli-input-json ã«æž¡ãã JSON ã衚瀺ãããŸããå®è£
ã®éã«ã¯ãã¡ããåèã«ããªããã ãŠãŒã¶ãŒã¬ã€ã ãåç
§ããŠå©çšãããæ©èœã«ããããèšå®ã远å ããŠããããšãããããããŸãã æ³šæç¹ã»ã€ãŸã¥ãããã€ã³ã å©çšåã« IAM ã§ MediaConvert çšããŒã«ã®èšå®ãå¿
èŠã§ã ã¹ããã 3. IAM æš©éã®èšå® AWS ã³ã³ãœãŒã«ã® Service Quotas > AWS ãµãŒãã¹ > AWS Elemental MediaConvert ãã確èªã§ããŸããããšã³ã³ãŒã䞊è¡åŠçã®åæå®è¡æ°äžé㯠20 ã«ãªã£ãŠããŸã AWS ã«ãŒãã¢ã«ãŠã³ã 1 ã€ã«ã€ã 1 ãµãŒãã¹ãå²åœãŠãããã®ã§ããããå¢ããããå Žåã¯ç³è«ãå¿
èŠã§ã ãšã³ã³ãŒããžã§ãããã¥ãŒã€ã³ã°ããããã¥ãŒããäœæããŠããžã§ãã®ç»é²æã«éžã¹ãã®ã§ãããäžèšããã䞊è¡åŠçã®åæå®è¡æ°äžéãã¯ãã®ããã¥ãŒãæ¯ã«åçã«æ¯ãåããããŸã äŸãã°ãæ¬çªãã¥ãŒããšãæ€èšŒãã¥ãŒãã® 2 ã€ã®ãã¥ãŒãäœæããå Žåãããããã®äžŠè¡åŠçã®åæå®è¡æ°äžé㯠10 ãã€ã«ãªãã®ã§æ³šæããŠãã ãã ãããã§ã¹ãæé圢åŒïŒManifest duration formatïŒã«æŽæ°ïŒINTEGERïŒãæå®ããŠãããšãiOS Safari ã§ãåç»ã®å®éã®åçæéãšãåçãã¬ã€ã€ãŒã®ã·ãŒã¯ããŒã«è¡šç€ºãããåèšæéã«ãºã¬ãçãããåé¡ããããŸãã æµ®åå°æ°ç¹ïŒFLOATING POINTïŒã«å€æŽããããšã§å¯Ÿå¿ããŸããããããã§ã¹ããã¡ã€ã«ã«åºåãããå .ts ãã¡ã€ã«ã®é·ãããæµ®åå°æ°ç¹ â æŽæ°ã«å€æããåãäžããããããšã§ãºã¬ãçããŠããããã§ãã hls.js ã«ãã HLS åç»ã®åçå¶åŸ¡ MediaConvert ã«ãã HLS 圢åŒã«ãšã³ã³ãŒããããåç»ããWeb ãã©ãŠã¶ã§åçããããã«å¿
èŠãªã®ããhls.js ã§ãã ref. video-dev/hls.js å®ã¯ HLS ã«ããã¹ããªãŒãã³ã°é
ä¿¡ã¯ãçŸç¶ Safari ãªã©éããããã©ãŠã¶ã§ãããã€ãã£ãã§ãµããŒããããŠããŸããã ref. https://caniuse.com/http-live-streaming ãã® HLS åç»ã Safari 以å€ã® Google Chrome ã IE11 ãªã©ã®äž»èŠãã©ãŠã¶ã§åçå¯èœã«ãããããhls.js ãå©çšãããŠããŸããå
éšçã«ã¯ãé察å¿ãã©ãŠã¶ç°å¢ã«ãããŠããã©ãŠã¶ã® MediaSource æ¡åŒµ ã䜿ã£ãŠ HLS åç»ãåçãã仿§ã«ãªã£ãŠããŸãã Video.js ãšã®æ¯èŒ 䌌ããããªã©ã€ãã©ãªã« Video.js ãšãããã®ããããå°å
¥ãè¿·ã£ãã®ã§ãã ⊠Video.js 㯠UI ãã»ããã«ãªã£ãã HLS ã«å¯Ÿå¿ããåçãã¬ã€ã€ãŒãã©ã€ãã©ãª HLS 察å¿ä»¥å€ã«ããåå¹ãç« åããªã©æ©èœãè±å¯ hls.js ã¯ãã©ãŠã¶æšæºã® <video> ã¿ã°ã§ HLS ã«å¯Ÿå¿ããããšã ããç®çã«ããã HLS ã¯ã©ã€ã¢ã³ããã©ã€ãã©ãª UI ãªã©ã¯ãªããåç»åçãã¬ã€ã€ãŒã¯ãã©ãŠã¶æšæºã®ãŸãŸ âŠãšãäžèšã®ããã« hls.js ã®æ¹ãã·ã³ãã«ã«ããããããšãå®çŸã§ãããããä»å㯠hls.js ãæ¡çšããŸããã GitHub ã®ã¹ã¿ãŒæ°ã¯å
çºã® Video.js ã®æ¹ãå€ãã®ã§ãããhls.js ãéçºã¯æŽ»çºã§ãæ¥æ¬ã§ã¯ ã°ãã·ãŒ ãããäžççã«ã¯ TED ã Twitter ã§ãæ¡çšãããŠãããååå®çžŸãããããšæããŸãã hls.js ã«ããå®è£
åºæ¬çã«ã¯ README ã® Getting Started ã®éãã§å®è£
ã§ããŸããäžéš README ã®ãµã³ãã«ã³ãŒãããæç²ããŠè§£èª¬ãããšâŠ var video = document . getElementById ( "video" ); var videoSrc = "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8" ; if ( Hls . isSupported ()) { var hls = new Hls (); hls . loadSource ( videoSrc ); hls . attachMedia ( video ); hls . on ( Hls . Events . MANIFEST_PARSED , function () { video . play (); }); } äžèš Hls.isSupported() ã®åå²ã§ãHLS ããã€ãã£ããµããŒãããŠããªããã©ãŠã¶ã®åŠçãå®è£
ããŠããŸãã æ¬æ¥ <video> ã® src 屿§ã«ã»ããããã¹ã .m3u8 ãã¡ã€ã«ã® URL ãž hls.loadSource() ã§ã¢ã¯ã»ã¹ãããã¯ã©ã€ã¢ã³ããã XHR ãªã¯ãšã¹ããé£ã°ããŸãããã®åŸ hls.attachMedia() ã§ã€ã³ã¹ã¿ã³ã¹ã DOM äžã® <video> ã¿ã°ã«çŽã¥ããŠããŸãã else if ( video . canPlayType ( 'application/vnd.apple.mpegurl' )) { video . src = videoSrc ; video . addEventListener ( 'loadedmetadata' , function () { video . play (); }); } äžèšã®åå²ã iOS Safari ãªã©ãHLS åç»ããã€ãã£ããµããŒãããŠãããã©ãŠã¶åãã®åŠçã§ããåçŽã« .m3u8 ãžã® URL ã <video> ã¿ã°ã® src ãžä»äžããŠããã ãã§ããã ïŒãµã³ãã«ã³ãŒãã§ã¯ããããã§ã¹ããã¡ã€ã«ã®ããŒãåŸã«èªååçãããããã«ãªã£ãŠããããã§ãïŒ æ³šæç¹ã»ã€ãŸã¥ãããã€ã³ã hls.js ã¯ã©ã€ã¢ã³ããååŸãã HLS åç»ãã¡ã€ã«çŸ€ã¯ãCORS ãããã§ GET ãªã¯ãšã¹ããèš±å¯ãããç°å¢ã«èšçœ®ããå¿
èŠããããŸã .m3u8 ãããã§ã¹ããã¡ã€ã«ãã¢ããªã® API ãªã©ããè¿åŽããå ŽåãContent-Type ã application/x-mpegURL ã«ããŠæž¡ãå¿
èŠããããŸã iOS Safari ãªã©ã® hls.js é察å¿ãã©ãŠã¶åãã®å®è£
ãæèããå¿
èŠããããŸã hls.js ã«ããå¶åŸ¡ãè€éã«ãªãã±ãŒã¹ã§ã¯ãåããããªå¶åŸ¡ã hls.js é察å¿ãã©ãŠã¶åãã«å®è£
ã§ãããïŒãã€ã¡ãŒãžã§ããªããšææ»ããçºçãããã§ã ãã®ä»ãããã³ããšã³ãã§ã¯ <video> ã¿ã°ã®ãã©ãŠã¶æ¯ã®æåãã衚瀺ã®éãã«æéãããããŸãããïŒããçšåºŠäºæ³ã¯ããŠããŸãããããã¯ãã¡ãã£ã¢ã®åãæ±ãã¯é£ããâŠïŒ hls.js èªäœã¯å°å
¥ãæè»œã§ããµã¯ããš HLS åç»ã®ãã«ããã©ãŠã¶å¯Ÿå¿ãå®çŸã§ãããšãŠã䜿ããããã£ãã§ãã@types ãååšããã®ã§ãTypeScript ç°å¢ã§ãé£ãªãå®è£
ã§ããŸããã SSR ã HLS + AES-128 ã®åçã«ã察å¿ããŠããã®ã§ãèå³ã®ããæ¹ã¯äžåºŠ å
¬åŒããã¥ã¡ã³ã ã確èªããŠã¿ãŠãã ããã ãããã« åŸæ¥ãåç»é
ä¿¡ãµãŒãã¹ãæ§ç¯ããå Žåãffmpeg ãèŒãããšã³ã³ãŒããµãŒãããã¹ããªãŒãã³ã°é
ä¿¡ãµãŒããå¥å»ºãŠããŠãè² è·ã«å¿ããŠã¹ã±ãŒã«ãããŠâŠã®ãããªèšèšãå¿
èŠã ã£ãããšæããŸãã ä»åãMediaConvert ãã¯ãããšãã AWS ãµãŒãã¹ãš hls.js ãå©çšããããšã§ãæè»œã«ãã¹ã±ãŒã©ãã«ãªåç»ãšã³ã³ãŒãïŒHTTP ã¹ããªãŒãã³ã°é
ä¿¡ç°å¢ãæ§ç¯ããããšãã§ããŸããã ãžã§ãã¡ãã¬ãŒã®åç»éžèã¯ãŸã ãªãªãŒã¹ããã°ããã§ãã®ã§ãä»åŸåé¿ãèŠãªããããããªãæ¹åãéããŠãããããšæããŸããæåŸãŸã§ãèªã¿ããã ãããããšãããããŸããã åéã®äžèЧ | æ ªåŒäŒç€Ÿã¡ãã¬ãŒ ã¡ãã¬ãŒã®æ¡çšæ
å ±ã¯ãã¡ãããã確èªãã ããã www.medley.jp
ããã«ã¡ã¯ã第äžéçºã°ã«ãŒãã®ç¢éã§ãããžã§ãã¡ãã¬ãŒéçºãšã³ãžãã¢ãšããŠãäž»ã«ããã¯ãšã³ããæ
åœããŠããŸãã çŽè¿ã§ã¯ããžã§ãã¡ãã¬ãŒãå
æãªãªãŒã¹ãã ãåç»éžèã æ©èœã®éçºãããžã§ã¯ãã«æºãã£ãŠãããåç»ãã¡ã€ã«ã®ã¢ããããŒãïŒé
ä¿¡ç°å¢ã®èšèšã»å®è£
ãè¡ã£ãŠããŸããã ä»åã®ããã°ã§ã¯ããã®ãåç»éžèãæ©èœã®éçºã«å©çšãã AWS Elemental MediaConvert ãµãŒãã¹ãšã hls.js ãšãã OSS ã©ã€ãã©ãªã«ã€ããŠç޹ä»ããããšæããŸãã ãžã§ãã¡ãã¬ãŒã®ãåç»éžèãæ©èœ ã¯ããã«ãä»åãªãªãŒã¹ãããåç»éžèãæ©èœã«ã€ããŠæŠèŠã玹ä»ããŸãã æ°åã³ãããŠã€ã«ã¹æææ¡å€§ã«ãã£ãŠã察é¢ã§ã®é¢æ¥ã«äžå®ãæããããå
Œ
±äº€éæ©é¢ã®å©çšãé£ãããªã£ããããããšã«ãããæºè¶³ãªè»¢è·æŽ»åãã§ããªããªã£ãŠããæ¹ãããã£ãããããšæããŸãã ãã®ãããªèª²é¡ã解決ããããã«ããžã§ãã¡ãã¬ãŒã§ã¯ãªã¢ã«ã¿ã€ã ã«ãªã³ã©ã€ã³ã§é¢æ¥ãè¡ããWEB 颿¥ããšãäºæ¥è
ããããããèšå®ãã質åã«å¯ŸããŠå¿åè
ãåç»ã§åçãéããåç»éžèãã® 2 ã€ã®æ©èœãæäŸéå§ããããŸããã ref. WEB 颿¥ã»åç»éžèæ©èœã®ãªãªãŒã¹ã®ãç¥ãã åç»éžèïŒåç»é¢æ¥ïŒã¯ãè¿å¹Žå¢å åŸåã«ãããªã³ã©ã€ã³éžèã®äžçš®ã§ããäžè¬çã«ãæ±è·è
/ 就掻çã PC ã»ã¹ããŒããã©ã³çã®ã«ã¡ã©ã§ãäºãçšæãããèšåã«å¿ããŠåç»ãæ®åœ±ããäŒæ¥ã«éãããšã§éžèãè¡ããŸãã ref. WEB 颿¥ã»åç»éžèãšã¯ïŒ 宿œã®æµãã䜿çšããŒã«ããããŒã泚æç¹ãªã©ã培åºè§£èª¬ïŒ ç§ãã¡ãžã§ãã¡ãã¬ãŒã®åç»éžèã§ã¯ãäºæ¥æããããããèšå®ãã質åã«å¯ŸããŠãæ±è·è
ãåçåç»ãæåºããããšãã§ããŸããäºæ¥æãæ±è·è
ããåç»ã§è³ªåã»åçãéãããšã§ãæžé¡ã ãã§ã¯äŒãããªãé°å²æ°ã匷ã¿ãçžæã«äŒããããšãã§ããŸãã WEB 颿¥ã»åç»éžèæ©èœã®ãªãªãŒã¹ã®ãç¥ãã åç»é
ä¿¡ãµãŒãã¹ã®èšèšãã€ã³ã Web ã¢ããªã§ãã®ãããªåç»é
ä¿¡ãµãŒãã¹ãéçºããå ŽåãããŠãŒã¶ã«ããåç»ã¢ããããŒãç°å¢ããšããŠãŒã¶ãžã®åç»ã®é
ä¿¡ã»åçç°å¢ããæäŸããå¿
èŠããããŸãã ãžã§ãã¡ãã¬ãŒã§æ±ãåç»ã¯äžè¬å
¬éããããã®ã§ã¯ãªããå
¬éæ¡ä»¶ãè€éã§ãã ãã£ãŠä»åã¯ããã®ãåç»ã¢ããããŒãïŒé
ä¿¡ç°å¢ããèªãµãŒãã¹å
ã«æ§ç¯ããæ¹éããšãã以äžã®ãããªåç»ãŸããã®èšèšãã€ã³ãã«ã€ããŠæ€èšã»æè¡éžå®ãè¡ãããšã«ããŸããã ïŒãã¡ãããèŠä»¶ã«ãã£ãŠã¯ YouTube ããæ³äººåãåç»é
ä¿¡ãã©ãããã©ãŒã ãå¥çŽããæ¹ãæè»œãªå ŽåãããããšæããŸãïŒ åç»ã®é²ç»ã»æ®åœ± ãµããŒããããåç»ãã¡ã€ã«ã®ãã©ãŒããããã©ãããã Web ã¢ããªå
ã«é²ç»æ©èœãèšããã åç»ã®ã¢ããããŒãïŒã¹ãã¬ãŒãžïŒ åç»ãã¡ã€ã«ã®ããªããŒã·ã§ã³ã§ãåç»ãã¡ã€ã«ã®è§£æããè¡ãã åç»ãã¡ã€ã«ã®ã¢ããããŒãå
ïŒã¹ãã¬ãŒãžïŒãã©ãã«ããã åç»ã®ãšã³ã³ãŒã åç»ãã¡ã€ã«ã®ãšã³ã³ãŒã圢åŒïŒH.264ãHLS çïŒãã©ãããã éåæãšã³ã³ãŒãã®å Žåãã¹ããŒã¿ã¹æ€ç¥ã»ãšã©ãŒãã³ããªã³ã°ãã©ãããã åç»ã®é
ä¿¡ïŒããŠã³ããŒãïŒ é
信圢åŒïŒããŠã³ããŒãïŒã¹ããªãŒãã³ã°ïŒãã©ãããã æå·åãããå Žåã埩å·ãã©ã®ããã«è¡ãã åç»ãã¡ã€ã«ã®å
¬éæ¹æ³ïŒã¢ã¯ã»ã¹å¶éïŒãã©ãããã åç»ã®åç Web ããŒãžäžã§åçãããã®ãããã®å Žåã®è¡šç€ºã»åçå¶åŸ¡ãã©ãããã ãã©ãŠã¶ãµããŒããã©ããŸã§ã«ããããé察å¿ã»ãšã©ãŒæã®å¶åŸ¡ãã©ãããã ä»åã¯ãäžèšã®å€ªåã§èšèŒãã ãåç»ã®ãšã³ã³ãŒããã« MediaConvert ãã ãåç»ã®åçãã« hls.js ãããããæ¡çšããŠããŸãã åé
ã®è©³çްã¯çããŸãããå
šäœãéããŠå€§ãŸãã«ã以äžã®ãããŒã§ãåç»ã¢ããããŒã â ãšã³ã³ãŒãïŒå€æïŒâ é
ä¿¡ã»åçããå®çŸããããšã«ããŸããã ãã©ãŠã¶ãã Ajax ã§åç»ã S3 ãžã¢ããããŒããã MediaConvert ãåç»ã HLS 圢åŒã«ãšã³ã³ãŒãïŒå€æïŒãã ãã©ãŠã¶ã§ hls.js ã䜿ãåç»ã CloudFront ããã¹ããªãŒãã³ã°åœ¢åŒã§åä¿¡ãåçãã ä»åã¯ãã®ãåç»ã¢ããããŒã â ãšã³ã³ãŒãïŒå€æïŒâ é
ä¿¡ã»åçãã«çŠç¹ãçµããMediaConvert ãš hls.js ãã©ã®ããã«äœ¿ã£ãã®ãã玹ä»ããŸãã MediaConvert ã«ãã HLS ãšã³ã³ãŒã AWS Elemental MediaConvert ã¯ãS3 ãšã®èŠªåæ§ãé«ããã¡ã€ã«ããŒã¹ã®åç»å€æãµãŒãã¹ã§ããèªåã§ ffmpeg ãªã©ã䜿ã£ãŠåç»ãšã³ã³ãŒããµãŒããæ§ç¯ã»ç®¡çããããšãªããã¹ã±ãŒã©ãã«ãªåç»å€æåŠçãæè»œã«ã·ã¹ãã ã«çµã¿èŸŒãããšãã§ããŸãã ref. AWS Elemental MediaConvert æéã¯åºåããåç»ã®åçæéã«å¿ããåŸé課éã§ããAWS ã³ã³ãœãŒã«ãã GUI ããŒã¹ã§ãšã³ã³ãŒãèšå®ãäœæãããããžã§ãïŒãšã³ã³ãŒãåŠçïŒãç»é²ããããšãã§ããŸãã ãŸããä» AWS ãµãŒãã¹åæ§ã« API ãæäŸãããŠãããAWS CLI ãåèšèªã® SDK ã䜿ã£ãŠããã°ã©ã ãããšã³ã³ãŒãåŠçãç»é²ããããšãã§ããã·ã¹ãã 飿ºã容æã§ãã # CLI ã§ãšã³ã³ãŒããžã§ããç»é²ããäŸ $ aws --endpoint-url https://abcd1234.mediaconvert.region-name-1.amazonaws.com --region region-name-1 mediaconvert create-job --cli-input-json file://~/job.json äžèš CLI ã³ãã³ãã§äžã®ãããªãšã³ã³ãŒãèšå®ãèšèŒãã JSON ã䜿ããžã§ããäœæãããšãS3 äžã®åç»ãã¡ã€ã«ããµã¯ããšãšã³ã³ãŒãããŠãããŸãããžã§ãã¯ãã¥ãŒã€ã³ã°ãããå
éšã§äžŠååŠçãããããã倧éã®ãšã³ã³ãŒãèŠæ±ã«ãç°¡åã«å¿ããããšãã§ããŸãã { ... "Settings" : { "Inputs" : [ { # å
¥åå
ã® S3 ãã±ããäžã®åç»ãã¡ã€ã« key ãæå® "FileInput" : "s3://testcontent/720/example_input_720p.mov" } ], "OutputGroups" : [ { "OutputGroupSettings" : { "FileGroupSettings" : { # åºåå
ã® S3 ãã±ãã key ãæå® "Destination" : "s3://testbucket/output" } }, # åç»ã»é³å£°ã®ãšã³ã³ãŒãèšå®ãæå® # ããã§å質ã¬ãã«æ¯ã«æ¯ãåããè€æ°ã®ãã¡ã€ã«ãåºåããã # ãµã ãã€ã« jpg ãäœæãããããããšãå¯èœ "Outputs" : [ { "VideoDescription" : { ⊠}, "AudioDescriptions" : { ⊠} } ] } ] } } ref. AWSCLI ã䜿çšãã AWSElemental MediaConvertCreateJob ã®äŸ ãšã³ã³ãŒããå®äºãããžã§ãã¯ãcron + SDK ãªã©ã§ API ãä»ããŠå®æãã§ãã¯ããä»ã«ãCloudWatch Events ã«ããã€ãã³ãç£èŠ â Lambda ã§åŠçãããããªããšãã§ããŸãã ref. AWS Elemental MediaConvert ã«ãã CloudWatch ã€ãã³ã ã®äœ¿çš ãªãåç»ãåãšã³ã³ãŒãããã®ã éåžžããŠãŒã¶ããã¢ããããŒããããåç»ãã¡ã€ã«ã¯ãæ¢ã«äœããã®ã³ãŒããã¯ã§å§çž®ãã .mp4 ã .mov ãªã©ã®ã³ã³ãããã©ãŒãããã«å€æãããŠããããšãæ®ã©ã§ãã ããã Web ããŒãžã§ <video> ã¿ã°ã䜿ããããåç»ãã¡ã€ã«ãåçããããšããå Žåã ãåç»ãã©ãŒãããã«ãã©ãŠã¶ãé察å¿ã ãšåçã§ããªãã ãšããç°å¢äŸååé¡ããããŸãã ãã©ãŠã¶ãšåç»ãã©ãŒãããã®ãµããŒã衚 ref. HTML5 video > Browser support ãã®åé¡ã«å¯Ÿå¿ãããããå€ãã®åç»é
ä¿¡ãµãŒãã¹ã§ã¯ããŠãŒã¶ã®åç»ãå€ãã®ç°å¢ã§åçå¯èœãª MP4 ã³ã³ãããã©ãŒãããïŒH.264 + AAC ã³ãŒããã¯ïŒãªã©ã®åœ¢åŒãžãåãšã³ã³ãŒããããŠããŸãã ãžã§ãã¡ãã¬ãŒã®åç»éžèã§ã¯äžèšç®çã«å ããŠãåç»é²èЧæã®åç·ã»ç«¯æ«è² è·ãæãã ãHTTP ã¹ããªãŒãã³ã°åœ¢åŒã ã§åç»ãé
ä¿¡ããããã«ãã¢ããããŒããããåç»ãå
šãŠ HLS åœ¢åŒ ã«ãšã³ã³ãŒãããŠããŸãã HLS - HTTP Live Streaming åœ¢åŒ HLS 㯠HTTP Live Streaming ã®ç¥ã§ãApple 瀟ã®éçºããèŠæ Œã§ããHTTP ããŒã¹ã®ã¹ããªãŒãã³ã°éä¿¡ãããã³ã«ã§ã现åãã«ãã MP4 åç»ãã¡ã€ã«ãåå²ããŠã³ããŒããããããšã§åç»ã®ã¹ããªãŒãã³ã°é
ä¿¡ãå®çŸããŠããŸãã HLS 圢åŒã«ãšã³ã³ãŒããããåç»ã¯ .ts ãšããåå²ãããã¡ãã£ã¢ãã¡ã€ã«çŸ€ãšã .m3u8 ãšãããã¡ãã£ã¢ãã¡ã€ã«ã®ååŸå
ãç§æ°ãªã©ãèšèŒããããã¹ããã¡ã€ã«ã§æ§æãããŸãã .m3u8 ãã¡ã€ã«ã®äŸïŒãããã§ã¹ããã¡ã€ã«ããã¬ã€ãªã¹ããã¡ã€ã«ãšãïŒ #EXTM3U #EXT-X-TARGETDURATION:10 #EXT-X-VERSION:3 #EXT-X-MEDIA-SEQUENCE:0 #EXT-X-PLAYLIST-TYPE:VOD #EXTINF:9.97663, media-0.ts #EXTINF:9.97663, media-1.ts #EXTINF:7.10710, media-2.ts #EXT-X-ENDLIST ref. RFC 8216: HTTP Live Streaming HLS ã¯ä»ã®ã¹ããªãŒãã³ã°åœ¢åŒãšæ¯èŒããŠãã©ã€ãé
ä¿¡ / VOD ã©ã¡ãã«ã察å¿å¯èœãªããšã察å¿ãã©ãŠã¶ãå€ãããšãå°çšã®é
ä¿¡ãµãŒãã䜿ããã«é
ä¿¡å¯èœãªããšãªã©ãããè¿å¹Žã®åç»é
ä¿¡ãµãŒãã¹ã§åºãå©çšãããŠããŸãã Web ãšã³ãžãã¢ã®èŠç¹ããèŠãŠãã HTTP ããŒã¹ãªãããã£ãã·ã¥ã HTTPS æå·åãªã©ãæ¢å Web æè¡ãšæãåãããããšãæ³åãããããæ±ããããå°è±¡ã§ããã MediaConvert ã® HLS ãšã³ã³ãŒããžã§ãèšå® å®éã«ããã°ã©ã ãã API çµç±ã§ HLS ãšã³ã³ãŒããžã§ããç»é²ããéã®èšå® JSON ã¯ã以äžã®ããã« GUI ã§ãžã§ããã³ãã¬ãŒããäœæããŠç¢ºèªããããšãã§ããŸãã ãã®ã JSON ã衚瀺ãã§ãåè¿°ãã CLI ã³ãã³ã mediaconvert create-job --cli-input-json ã«æž¡ãã JSON ã衚瀺ãããŸããå®è£
ã®éã«ã¯ãã¡ããåèã«ããªããã ãŠãŒã¶ãŒã¬ã€ã ãåç
§ããŠå©çšãããæ©èœã«ããããèšå®ã远å ããŠããããšãããããããŸãã æ³šæç¹ã»ã€ãŸã¥ãããã€ã³ã å©çšåã« IAM ã§ MediaConvert çšããŒã«ã®èšå®ãå¿
èŠã§ã ã¹ããã 3. IAM æš©éã®èšå® AWS ã³ã³ãœãŒã«ã® Service Quotas > AWS ãµãŒãã¹ > AWS Elemental MediaConvert ãã確èªã§ããŸããããšã³ã³ãŒã䞊è¡åŠçã®åæå®è¡æ°äžé㯠20 ã«ãªã£ãŠããŸã AWS ã«ãŒãã¢ã«ãŠã³ã 1 ã€ã«ã€ã 1 ãµãŒãã¹ãå²åœãŠãããã®ã§ããããå¢ããããå Žåã¯ç³è«ãå¿
èŠã§ã ãšã³ã³ãŒããžã§ãããã¥ãŒã€ã³ã°ããããã¥ãŒããäœæããŠããžã§ãã®ç»é²æã«éžã¹ãã®ã§ãããäžèšããã䞊è¡åŠçã®åæå®è¡æ°äžéãã¯ãã®ããã¥ãŒãæ¯ã«åçã«æ¯ãåããããŸã äŸãã°ãæ¬çªãã¥ãŒããšãæ€èšŒãã¥ãŒãã® 2 ã€ã®ãã¥ãŒãäœæããå Žåãããããã®äžŠè¡åŠçã®åæå®è¡æ°äžé㯠10 ãã€ã«ãªãã®ã§æ³šæããŠãã ãã ãããã§ã¹ãæé圢åŒïŒManifest duration formatïŒã«æŽæ°ïŒINTEGERïŒãæå®ããŠãããšãiOS Safari ã§ãåç»ã®å®éã®åçæéãšãåçãã¬ã€ã€ãŒã®ã·ãŒã¯ããŒã«è¡šç€ºãããåèšæéã«ãºã¬ãçãããåé¡ããããŸãã æµ®åå°æ°ç¹ïŒFLOATING POINTïŒã«å€æŽããããšã§å¯Ÿå¿ããŸããããããã§ã¹ããã¡ã€ã«ã«åºåãããå .ts ãã¡ã€ã«ã®é·ãããæµ®åå°æ°ç¹ â æŽæ°ã«å€æããåãäžããããããšã§ãºã¬ãçããŠããããã§ãã hls.js ã«ãã HLS åç»ã®åçå¶åŸ¡ MediaConvert ã«ãã HLS 圢åŒã«ãšã³ã³ãŒããããåç»ããWeb ãã©ãŠã¶ã§åçããããã«å¿
èŠãªã®ããhls.js ã§ãã ref. video-dev/hls.js å®ã¯ HLS ã«ããã¹ããªãŒãã³ã°é
ä¿¡ã¯ãçŸç¶ Safari ãªã©éããããã©ãŠã¶ã§ãããã€ãã£ãã§ãµããŒããããŠããŸããã ref. https://caniuse.com/http-live-streaming ãã® HLS åç»ã Safari 以å€ã® Google Chrome ã IE11 ãªã©ã®äž»èŠãã©ãŠã¶ã§åçå¯èœã«ãããããhls.js ãå©çšãããŠããŸããå
éšçã«ã¯ãé察å¿ãã©ãŠã¶ç°å¢ã«ãããŠããã©ãŠã¶ã® MediaSource æ¡åŒµ ã䜿ã£ãŠ HLS åç»ãåçãã仿§ã«ãªã£ãŠããŸãã Video.js ãšã®æ¯èŒ 䌌ããããªã©ã€ãã©ãªã« Video.js ãšãããã®ããããå°å
¥ãè¿·ã£ãã®ã§ãã ⊠Video.js 㯠UI ãã»ããã«ãªã£ãã HLS ã«å¯Ÿå¿ããåçãã¬ã€ã€ãŒãã©ã€ãã©ãª HLS 察å¿ä»¥å€ã«ããåå¹ãç« åããªã©æ©èœãè±å¯ hls.js ã¯ãã©ãŠã¶æšæºã® <video> ã¿ã°ã§ HLS ã«å¯Ÿå¿ããããšã ããç®çã«ããã HLS ã¯ã©ã€ã¢ã³ããã©ã€ãã©ãª UI ãªã©ã¯ãªããåç»åçãã¬ã€ã€ãŒã¯ãã©ãŠã¶æšæºã®ãŸãŸ âŠãšãäžèšã®ããã« hls.js ã®æ¹ãã·ã³ãã«ã«ããããããšãå®çŸã§ãããããä»å㯠hls.js ãæ¡çšããŸããã GitHub ã®ã¹ã¿ãŒæ°ã¯å
çºã® Video.js ã®æ¹ãå€ãã®ã§ãããhls.js ãéçºã¯æŽ»çºã§ãæ¥æ¬ã§ã¯ ã°ãã·ãŒ ãããäžççã«ã¯ TED ã Twitter ã§ãæ¡çšãããŠãããååå®çžŸãããããšæããŸãã hls.js ã«ããå®è£
åºæ¬çã«ã¯ README ã® Getting Started ã®éãã§å®è£
ã§ããŸããäžéš README ã®ãµã³ãã«ã³ãŒãããæç²ããŠè§£èª¬ãããšâŠ var video = document . getElementById ( "video" ); var videoSrc = "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8" ; if ( Hls . isSupported ()) { var hls = new Hls (); hls . loadSource ( videoSrc ); hls . attachMedia ( video ); hls . on ( Hls . Events . MANIFEST_PARSED , function () { video . play (); }); } äžèš Hls.isSupported() ã®åå²ã§ãHLS ããã€ãã£ããµããŒãããŠããªããã©ãŠã¶ã®åŠçãå®è£
ããŠããŸãã æ¬æ¥ <video> ã® src 屿§ã«ã»ããããã¹ã .m3u8 ãã¡ã€ã«ã® URL ãž hls.loadSource() ã§ã¢ã¯ã»ã¹ãããã¯ã©ã€ã¢ã³ããã XHR ãªã¯ãšã¹ããé£ã°ããŸãããã®åŸ hls.attachMedia() ã§ã€ã³ã¹ã¿ã³ã¹ã DOM äžã® <video> ã¿ã°ã«çŽã¥ããŠããŸãã else if ( video . canPlayType ( 'application/vnd.apple.mpegurl' )) { video . src = videoSrc ; video . addEventListener ( 'loadedmetadata' , function () { video . play (); }); } äžèšã®åå²ã iOS Safari ãªã©ãHLS åç»ããã€ãã£ããµããŒãããŠãããã©ãŠã¶åãã®åŠçã§ããåçŽã« .m3u8 ãžã® URL ã <video> ã¿ã°ã® src ãžä»äžããŠããã ãã§ããã ïŒãµã³ãã«ã³ãŒãã§ã¯ããããã§ã¹ããã¡ã€ã«ã®ããŒãåŸã«èªååçãããããã«ãªã£ãŠããããã§ãïŒ æ³šæç¹ã»ã€ãŸã¥ãããã€ã³ã hls.js ã¯ã©ã€ã¢ã³ããååŸãã HLS åç»ãã¡ã€ã«çŸ€ã¯ãCORS ãããã§ GET ãªã¯ãšã¹ããèš±å¯ãããç°å¢ã«èšçœ®ããå¿
èŠããããŸã .m3u8 ãããã§ã¹ããã¡ã€ã«ãã¢ããªã® API ãªã©ããè¿åŽããå ŽåãContent-Type ã application/x-mpegURL ã«ããŠæž¡ãå¿
èŠããããŸã iOS Safari ãªã©ã® hls.js é察å¿ãã©ãŠã¶åãã®å®è£
ãæèããå¿
èŠããããŸã hls.js ã«ããå¶åŸ¡ãè€éã«ãªãã±ãŒã¹ã§ã¯ãåããããªå¶åŸ¡ã hls.js é察å¿ãã©ãŠã¶åãã«å®è£
ã§ãããïŒãã€ã¡ãŒãžã§ããªããšææ»ããçºçãããã§ã ãã®ä»ãããã³ããšã³ãã§ã¯ <video> ã¿ã°ã®ãã©ãŠã¶æ¯ã®æåãã衚瀺ã®éãã«æéãããããŸãããïŒããçšåºŠäºæ³ã¯ããŠããŸãããããã¯ãã¡ãã£ã¢ã®åãæ±ãã¯é£ããâŠïŒ hls.js èªäœã¯å°å
¥ãæè»œã§ããµã¯ããš HLS åç»ã®ãã«ããã©ãŠã¶å¯Ÿå¿ãå®çŸã§ãããšãŠã䜿ããããã£ãã§ãã@types ãååšããã®ã§ãTypeScript ç°å¢ã§ãé£ãªãå®è£
ã§ããŸããã SSR ã HLS + AES-128 ã®åçã«ã察å¿ããŠããã®ã§ãèå³ã®ããæ¹ã¯äžåºŠ å
¬åŒããã¥ã¡ã³ã ã確èªããŠã¿ãŠãã ããã ãããã« åŸæ¥ãåç»é
ä¿¡ãµãŒãã¹ãæ§ç¯ããå Žåãffmpeg ãèŒãããšã³ã³ãŒããµãŒãããã¹ããªãŒãã³ã°é
ä¿¡ãµãŒããå¥å»ºãŠããŠãè² è·ã«å¿ããŠã¹ã±ãŒã«ãããŠâŠã®ãããªèšèšãå¿
èŠã ã£ãããšæããŸãã ä»åãMediaConvert ãã¯ãããšãã AWS ãµãŒãã¹ãš hls.js ãå©çšããããšã§ãæè»œã«ãã¹ã±ãŒã©ãã«ãªåç»ãšã³ã³ãŒãïŒHTTP ã¹ããªãŒãã³ã°é
ä¿¡ç°å¢ãæ§ç¯ããããšãã§ããŸããã ãžã§ãã¡ãã¬ãŒã®åç»éžèã¯ãŸã ãªãªãŒã¹ããã°ããã§ãã®ã§ãä»åŸåé¿ãèŠãªããããããªãæ¹åãéããŠãããããšæããŸããæåŸãŸã§ãèªã¿ããã ãããããšãããããŸããã åéã®äžèЧ | æ ªåŒäŒç€Ÿã¡ãã¬ãŒ ã¡ãã¬ãŒã®æ¡çšæ
å ±ã¯ãã¡ãããã確èªãã ããã www.medley.jp
ããã«ã¡ã¯ã第äžéçºã°ã«ãŒãã®ç¢éã§ãããžã§ãã¡ãã¬ãŒéçºãšã³ãžãã¢ãšããŠãäž»ã«ããã¯ãšã³ããæ
åœããŠããŸãã çŽè¿ã§ã¯ããžã§ãã¡ãã¬ãŒãå
æãªãªãŒã¹ãã ãåç»éžèã æ©èœã®éçºãããžã§ã¯ãã«æºãã£ãŠãããåç»ãã¡ã€ã«ã®ã¢ããããŒãïŒé
ä¿¡ç°å¢ã®èšèšã»å®è£
ãè¡ã£ãŠããŸããã ä»åã®ããã°ã§ã¯ããã®ãåç»éžèãæ©èœã®éçºã«å©çšãã AWS Elemental MediaConvert ãµãŒãã¹ãšã hls.js ãšãã OSS ã©ã€ãã©ãªã«ã€ããŠç޹ä»ããããšæããŸãã ãžã§ãã¡ãã¬ãŒã®ãåç»éžèãæ©èœ ã¯ããã«ãä»åãªãªãŒã¹ãããåç»éžèãæ©èœã«ã€ããŠæŠèŠã玹ä»ããŸãã æ°åã³ãããŠã€ã«ã¹æææ¡å€§ã«ãã£ãŠã察é¢ã§ã®é¢æ¥ã«äžå®ãæããããå
Œ
±äº€éæ©é¢ã®å©çšãé£ãããªã£ããããããšã«ãããæºè¶³ãªè»¢è·æŽ»åãã§ããªããªã£ãŠããæ¹ãããã£ãããããšæããŸãã ãã®ãããªèª²é¡ã解決ããããã«ããžã§ãã¡ãã¬ãŒã§ã¯ãªã¢ã«ã¿ã€ã ã«ãªã³ã©ã€ã³ã§é¢æ¥ãè¡ããWEB 颿¥ããšãäºæ¥è
ããããããèšå®ãã質åã«å¯ŸããŠå¿åè
ãåç»ã§åçãéããåç»éžèãã® 2 ã€ã®æ©èœãæäŸéå§ããããŸããã ref. WEB 颿¥ã»åç»éžèæ©èœã®ãªãªãŒã¹ã®ãç¥ãã åç»éžèïŒåç»é¢æ¥ïŒã¯ãè¿å¹Žå¢å åŸåã«ãããªã³ã©ã€ã³éžèã®äžçš®ã§ããäžè¬çã«ãæ±è·è
/ 就掻çã PC ã»ã¹ããŒããã©ã³çã®ã«ã¡ã©ã§ãäºãçšæãããèšåã«å¿ããŠåç»ãæ®åœ±ããäŒæ¥ã«éãããšã§éžèãè¡ããŸãã ref. WEB 颿¥ã»åç»éžèãšã¯ïŒ 宿œã®æµãã䜿çšããŒã«ããããŒã泚æç¹ãªã©ã培åºè§£èª¬ïŒ ç§ãã¡ãžã§ãã¡ãã¬ãŒã®åç»éžèã§ã¯ãäºæ¥æããããããèšå®ãã質åã«å¯ŸããŠãæ±è·è
ãåçåç»ãæåºããããšãã§ããŸããäºæ¥æãæ±è·è
ããåç»ã§è³ªåã»åçãéãããšã§ãæžé¡ã ãã§ã¯äŒãããªãé°å²æ°ã匷ã¿ãçžæã«äŒããããšãã§ããŸãã WEB 颿¥ã»åç»éžèæ©èœã®ãªãªãŒã¹ã®ãç¥ãã åç»é
ä¿¡ãµãŒãã¹ã®èšèšãã€ã³ã Web ã¢ããªã§ãã®ãããªåç»é
ä¿¡ãµãŒãã¹ãéçºããå ŽåãããŠãŒã¶ã«ããåç»ã¢ããããŒãç°å¢ããšããŠãŒã¶ãžã®åç»ã®é
ä¿¡ã»åçç°å¢ããæäŸããå¿
èŠããããŸãã ãžã§ãã¡ãã¬ãŒã§æ±ãåç»ã¯äžè¬å
¬éããããã®ã§ã¯ãªããå
¬éæ¡ä»¶ãè€éã§ãã ãã£ãŠä»åã¯ããã®ãåç»ã¢ããããŒãïŒé
ä¿¡ç°å¢ããèªãµãŒãã¹å
ã«æ§ç¯ããæ¹éããšãã以äžã®ãããªåç»ãŸããã®èšèšãã€ã³ãã«ã€ããŠæ€èšã»æè¡éžå®ãè¡ãããšã«ããŸããã ïŒãã¡ãããèŠä»¶ã«ãã£ãŠã¯ YouTube ããæ³äººåãåç»é
ä¿¡ãã©ãããã©ãŒã ãå¥çŽããæ¹ãæè»œãªå ŽåãããããšæããŸãïŒ åç»ã®é²ç»ã»æ®åœ± ãµããŒããããåç»ãã¡ã€ã«ã®ãã©ãŒããããã©ãããã Web ã¢ããªå
ã«é²ç»æ©èœãèšããã åç»ã®ã¢ããããŒãïŒã¹ãã¬ãŒãžïŒ åç»ãã¡ã€ã«ã®ããªããŒã·ã§ã³ã§ãåç»ãã¡ã€ã«ã®è§£æããè¡ãã åç»ãã¡ã€ã«ã®ã¢ããããŒãå
ïŒã¹ãã¬ãŒãžïŒãã©ãã«ããã åç»ã®ãšã³ã³ãŒã åç»ãã¡ã€ã«ã®ãšã³ã³ãŒã圢åŒïŒH.264ãHLS çïŒãã©ãããã éåæãšã³ã³ãŒãã®å Žåãã¹ããŒã¿ã¹æ€ç¥ã»ãšã©ãŒãã³ããªã³ã°ãã©ãããã åç»ã®é
ä¿¡ïŒããŠã³ããŒãïŒ é
信圢åŒïŒããŠã³ããŒãïŒã¹ããªãŒãã³ã°ïŒãã©ãããã æå·åãããå Žåã埩å·ãã©ã®ããã«è¡ãã åç»ãã¡ã€ã«ã®å
¬éæ¹æ³ïŒã¢ã¯ã»ã¹å¶éïŒãã©ãããã åç»ã®åç Web ããŒãžäžã§åçãããã®ãããã®å Žåã®è¡šç€ºã»åçå¶åŸ¡ãã©ãããã ãã©ãŠã¶ãµããŒããã©ããŸã§ã«ããããé察å¿ã»ãšã©ãŒæã®å¶åŸ¡ãã©ãããã ä»åã¯ãäžèšã®å€ªåã§èšèŒãã ãåç»ã®ãšã³ã³ãŒããã« MediaConvert ãã ãåç»ã®åçãã« hls.js ãããããæ¡çšããŠããŸãã åé
ã®è©³çްã¯çããŸãããå
šäœãéããŠå€§ãŸãã«ã以äžã®ãããŒã§ãåç»ã¢ããããŒã â ãšã³ã³ãŒãïŒå€æïŒâ é
ä¿¡ã»åçããå®çŸããããšã«ããŸããã ãã©ãŠã¶ãã Ajax ã§åç»ã S3 ãžã¢ããããŒããã MediaConvert ãåç»ã HLS 圢åŒã«ãšã³ã³ãŒãïŒå€æïŒãã ãã©ãŠã¶ã§ hls.js ã䜿ãåç»ã CloudFront ããã¹ããªãŒãã³ã°åœ¢åŒã§åä¿¡ãåçãã ä»åã¯ãã®ãåç»ã¢ããããŒã â ãšã³ã³ãŒãïŒå€æïŒâ é
ä¿¡ã»åçãã«çŠç¹ãçµããMediaConvert ãš hls.js ãã©ã®ããã«äœ¿ã£ãã®ãã玹ä»ããŸãã MediaConvert ã«ãã HLS ãšã³ã³ãŒã AWS Elemental MediaConvert ã¯ãS3 ãšã®èŠªåæ§ãé«ããã¡ã€ã«ããŒã¹ã®åç»å€æãµãŒãã¹ã§ããèªåã§ ffmpeg ãªã©ã䜿ã£ãŠåç»ãšã³ã³ãŒããµãŒããæ§ç¯ã»ç®¡çããããšãªããã¹ã±ãŒã©ãã«ãªåç»å€æåŠçãæè»œã«ã·ã¹ãã ã«çµã¿èŸŒãããšãã§ããŸãã ref. AWS Elemental MediaConvert æéã¯åºåããåç»ã®åçæéã«å¿ããåŸé課éã§ããAWS ã³ã³ãœãŒã«ãã GUI ããŒã¹ã§ãšã³ã³ãŒãèšå®ãäœæãããããžã§ãïŒãšã³ã³ãŒãåŠçïŒãç»é²ããããšãã§ããŸãã ãŸããä» AWS ãµãŒãã¹åæ§ã« API ãæäŸãããŠãããAWS CLI ãåèšèªã® SDK ã䜿ã£ãŠããã°ã©ã ãããšã³ã³ãŒãåŠçãç»é²ããããšãã§ããã·ã¹ãã 飿ºã容æã§ãã # CLI ã§ãšã³ã³ãŒããžã§ããç»é²ããäŸ $ aws --endpoint-url https://abcd1234.mediaconvert.region-name-1.amazonaws.com --region region-name-1 mediaconvert create-job --cli-input-json file://~/job.json äžèš CLI ã³ãã³ãã§äžã®ãããªãšã³ã³ãŒãèšå®ãèšèŒãã JSON ã䜿ããžã§ããäœæãããšãS3 äžã®åç»ãã¡ã€ã«ããµã¯ããšãšã³ã³ãŒãããŠãããŸãããžã§ãã¯ãã¥ãŒã€ã³ã°ãããå
éšã§äžŠååŠçãããããã倧éã®ãšã³ã³ãŒãèŠæ±ã«ãç°¡åã«å¿ããããšãã§ããŸãã { ... "Settings" : { "Inputs" : [ { # å
¥åå
ã® S3 ãã±ããäžã®åç»ãã¡ã€ã« key ãæå® "FileInput" : "s3://testcontent/720/example_input_720p.mov" } ], "OutputGroups" : [ { "OutputGroupSettings" : { "FileGroupSettings" : { # åºåå
ã® S3 ãã±ãã key ãæå® "Destination" : "s3://testbucket/output" } }, # åç»ã»é³å£°ã®ãšã³ã³ãŒãèšå®ãæå® # ããã§å質ã¬ãã«æ¯ã«æ¯ãåããè€æ°ã®ãã¡ã€ã«ãåºåããã # ãµã ãã€ã« jpg ãäœæãããããããšãå¯èœ "Outputs" : [ { "VideoDescription" : { ⊠}, "AudioDescriptions" : { ⊠} } ] } ] } } ref. AWSCLI ã䜿çšãã AWSElemental MediaConvertCreateJob ã®äŸ ãšã³ã³ãŒããå®äºãããžã§ãã¯ãcron + SDK ãªã©ã§ API ãä»ããŠå®æãã§ãã¯ããä»ã«ãCloudWatch Events ã«ããã€ãã³ãç£èŠ â Lambda ã§åŠçãããããªããšãã§ããŸãã ref. AWS Elemental MediaConvert ã«ãã CloudWatch ã€ãã³ã ã®äœ¿çš ãªãåç»ãåãšã³ã³ãŒãããã®ã éåžžããŠãŒã¶ããã¢ããããŒããããåç»ãã¡ã€ã«ã¯ãæ¢ã«äœããã®ã³ãŒããã¯ã§å§çž®ãã .mp4 ã .mov ãªã©ã®ã³ã³ãããã©ãŒãããã«å€æãããŠããããšãæ®ã©ã§ãã ããã Web ããŒãžã§ <video> ã¿ã°ã䜿ããããåç»ãã¡ã€ã«ãåçããããšããå Žåã ãåç»ãã©ãŒãããã«ãã©ãŠã¶ãé察å¿ã ãšåçã§ããªãã ãšããç°å¢äŸååé¡ããããŸãã ãã©ãŠã¶ãšåç»ãã©ãŒãããã®ãµããŒã衚 ref. HTML5 video > Browser support ãã®åé¡ã«å¯Ÿå¿ãããããå€ãã®åç»é
ä¿¡ãµãŒãã¹ã§ã¯ããŠãŒã¶ã®åç»ãå€ãã®ç°å¢ã§åçå¯èœãª MP4 ã³ã³ãããã©ãŒãããïŒH.264 + AAC ã³ãŒããã¯ïŒãªã©ã®åœ¢åŒãžãåãšã³ã³ãŒããããŠããŸãã ãžã§ãã¡ãã¬ãŒã®åç»éžèã§ã¯äžèšç®çã«å ããŠãåç»é²èЧæã®åç·ã»ç«¯æ«è² è·ãæãã ãHTTP ã¹ããªãŒãã³ã°åœ¢åŒã ã§åç»ãé
ä¿¡ããããã«ãã¢ããããŒããããåç»ãå
šãŠ HLS åœ¢åŒ ã«ãšã³ã³ãŒãããŠããŸãã HLS - HTTP Live Streaming åœ¢åŒ HLS 㯠HTTP Live Streaming ã®ç¥ã§ãApple 瀟ã®éçºããèŠæ Œã§ããHTTP ããŒã¹ã®ã¹ããªãŒãã³ã°éä¿¡ãããã³ã«ã§ã现åãã«ãã MP4 åç»ãã¡ã€ã«ãåå²ããŠã³ããŒããããããšã§åç»ã®ã¹ããªãŒãã³ã°é
ä¿¡ãå®çŸããŠããŸãã HLS 圢åŒã«ãšã³ã³ãŒããããåç»ã¯ .ts ãšããåå²ãããã¡ãã£ã¢ãã¡ã€ã«çŸ€ãšã .m3u8 ãšãããã¡ãã£ã¢ãã¡ã€ã«ã®ååŸå
ãç§æ°ãªã©ãèšèŒããããã¹ããã¡ã€ã«ã§æ§æãããŸãã .m3u8 ãã¡ã€ã«ã®äŸïŒãããã§ã¹ããã¡ã€ã«ããã¬ã€ãªã¹ããã¡ã€ã«ãšãïŒ #EXTM3U #EXT-X-TARGETDURATION:10 #EXT-X-VERSION:3 #EXT-X-MEDIA-SEQUENCE:0 #EXT-X-PLAYLIST-TYPE:VOD #EXTINF:9.97663, media-0.ts #EXTINF:9.97663, media-1.ts #EXTINF:7.10710, media-2.ts #EXT-X-ENDLIST ref. RFC 8216: HTTP Live Streaming HLS ã¯ä»ã®ã¹ããªãŒãã³ã°åœ¢åŒãšæ¯èŒããŠãã©ã€ãé
ä¿¡ / VOD ã©ã¡ãã«ã察å¿å¯èœãªããšã察å¿ãã©ãŠã¶ãå€ãããšãå°çšã®é
ä¿¡ãµãŒãã䜿ããã«é
ä¿¡å¯èœãªããšãªã©ãããè¿å¹Žã®åç»é
ä¿¡ãµãŒãã¹ã§åºãå©çšãããŠããŸãã Web ãšã³ãžãã¢ã®èŠç¹ããèŠãŠãã HTTP ããŒã¹ãªãããã£ãã·ã¥ã HTTPS æå·åãªã©ãæ¢å Web æè¡ãšæãåãããããšãæ³åãããããæ±ããããå°è±¡ã§ããã MediaConvert ã® HLS ãšã³ã³ãŒããžã§ãèšå® å®éã«ããã°ã©ã ãã API çµç±ã§ HLS ãšã³ã³ãŒããžã§ããç»é²ããéã®èšå® JSON ã¯ã以äžã®ããã« GUI ã§ãžã§ããã³ãã¬ãŒããäœæããŠç¢ºèªããããšãã§ããŸãã ãã®ã JSON ã衚瀺ãã§ãåè¿°ãã CLI ã³ãã³ã mediaconvert create-job --cli-input-json ã«æž¡ãã JSON ã衚瀺ãããŸããå®è£
ã®éã«ã¯ãã¡ããåèã«ããªããã ãŠãŒã¶ãŒã¬ã€ã ãåç
§ããŠå©çšãããæ©èœã«ããããèšå®ã远å ããŠããããšãããããããŸãã æ³šæç¹ã»ã€ãŸã¥ãããã€ã³ã å©çšåã« IAM ã§ MediaConvert çšããŒã«ã®èšå®ãå¿
èŠã§ã ã¹ããã 3. IAM æš©éã®èšå® AWS ã³ã³ãœãŒã«ã® Service Quotas > AWS ãµãŒãã¹ > AWS Elemental MediaConvert ãã確èªã§ããŸããããšã³ã³ãŒã䞊è¡åŠçã®åæå®è¡æ°äžé㯠20 ã«ãªã£ãŠããŸã AWS ã«ãŒãã¢ã«ãŠã³ã 1 ã€ã«ã€ã 1 ãµãŒãã¹ãå²åœãŠãããã®ã§ããããå¢ããããå Žåã¯ç³è«ãå¿
èŠã§ã ãšã³ã³ãŒããžã§ãããã¥ãŒã€ã³ã°ããããã¥ãŒããäœæããŠããžã§ãã®ç»é²æã«éžã¹ãã®ã§ãããäžèšããã䞊è¡åŠçã®åæå®è¡æ°äžéãã¯ãã®ããã¥ãŒãæ¯ã«åçã«æ¯ãåããããŸã äŸãã°ãæ¬çªãã¥ãŒããšãæ€èšŒãã¥ãŒãã® 2 ã€ã®ãã¥ãŒãäœæããå Žåãããããã®äžŠè¡åŠçã®åæå®è¡æ°äžé㯠10 ãã€ã«ãªãã®ã§æ³šæããŠãã ãã ãããã§ã¹ãæé圢åŒïŒManifest duration formatïŒã«æŽæ°ïŒINTEGERïŒãæå®ããŠãããšãiOS Safari ã§ãåç»ã®å®éã®åçæéãšãåçãã¬ã€ã€ãŒã®ã·ãŒã¯ããŒã«è¡šç€ºãããåèšæéã«ãºã¬ãçãããåé¡ããããŸãã æµ®åå°æ°ç¹ïŒFLOATING POINTïŒã«å€æŽããããšã§å¯Ÿå¿ããŸããããããã§ã¹ããã¡ã€ã«ã«åºåãããå .ts ãã¡ã€ã«ã®é·ãããæµ®åå°æ°ç¹ â æŽæ°ã«å€æããåãäžããããããšã§ãºã¬ãçããŠããããã§ãã hls.js ã«ãã HLS åç»ã®åçå¶åŸ¡ MediaConvert ã«ãã HLS 圢åŒã«ãšã³ã³ãŒããããåç»ããWeb ãã©ãŠã¶ã§åçããããã«å¿
èŠãªã®ããhls.js ã§ãã ref. video-dev/hls.js å®ã¯ HLS ã«ããã¹ããªãŒãã³ã°é
ä¿¡ã¯ãçŸç¶ Safari ãªã©éããããã©ãŠã¶ã§ãããã€ãã£ãã§ãµããŒããããŠããŸããã ref. https://caniuse.com/http-live-streaming ãã® HLS åç»ã Safari 以å€ã® Google Chrome ã IE11 ãªã©ã®äž»èŠãã©ãŠã¶ã§åçå¯èœã«ãããããhls.js ãå©çšãããŠããŸããå
éšçã«ã¯ãé察å¿ãã©ãŠã¶ç°å¢ã«ãããŠããã©ãŠã¶ã® MediaSource æ¡åŒµ ã䜿ã£ãŠ HLS åç»ãåçãã仿§ã«ãªã£ãŠããŸãã Video.js ãšã®æ¯èŒ 䌌ããããªã©ã€ãã©ãªã« Video.js ãšãããã®ããããå°å
¥ãè¿·ã£ãã®ã§ãã ⊠Video.js 㯠UI ãã»ããã«ãªã£ãã HLS ã«å¯Ÿå¿ããåçãã¬ã€ã€ãŒãã©ã€ãã©ãª HLS 察å¿ä»¥å€ã«ããåå¹ãç« åããªã©æ©èœãè±å¯ hls.js ã¯ãã©ãŠã¶æšæºã® <video> ã¿ã°ã§ HLS ã«å¯Ÿå¿ããããšã ããç®çã«ããã HLS ã¯ã©ã€ã¢ã³ããã©ã€ãã©ãª UI ãªã©ã¯ãªããåç»åçãã¬ã€ã€ãŒã¯ãã©ãŠã¶æšæºã®ãŸãŸ âŠãšãäžèšã®ããã« hls.js ã®æ¹ãã·ã³ãã«ã«ããããããšãå®çŸã§ãããããä»å㯠hls.js ãæ¡çšããŸããã GitHub ã®ã¹ã¿ãŒæ°ã¯å
çºã® Video.js ã®æ¹ãå€ãã®ã§ãããhls.js ãéçºã¯æŽ»çºã§ãæ¥æ¬ã§ã¯ ã°ãã·ãŒ ãããäžççã«ã¯ TED ã Twitter ã§ãæ¡çšãããŠãããååå®çžŸãããããšæããŸãã hls.js ã«ããå®è£
åºæ¬çã«ã¯ README ã® Getting Started ã®éãã§å®è£
ã§ããŸããäžéš README ã®ãµã³ãã«ã³ãŒãããæç²ããŠè§£èª¬ãããšâŠ var video = document . getElementById ( "video" ); var videoSrc = "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8" ; if ( Hls . isSupported ()) { var hls = new Hls (); hls . loadSource ( videoSrc ); hls . attachMedia ( video ); hls . on ( Hls . Events . MANIFEST_PARSED , function () { video . play (); }); } äžèš Hls.isSupported() ã®åå²ã§ãHLS ããã€ãã£ããµããŒãããŠããªããã©ãŠã¶ã®åŠçãå®è£
ããŠããŸãã æ¬æ¥ <video> ã® src 屿§ã«ã»ããããã¹ã .m3u8 ãã¡ã€ã«ã® URL ãž hls.loadSource() ã§ã¢ã¯ã»ã¹ãããã¯ã©ã€ã¢ã³ããã XHR ãªã¯ãšã¹ããé£ã°ããŸãããã®åŸ hls.attachMedia() ã§ã€ã³ã¹ã¿ã³ã¹ã DOM äžã® <video> ã¿ã°ã«çŽã¥ããŠããŸãã else if ( video . canPlayType ( 'application/vnd.apple.mpegurl' )) { video . src = videoSrc ; video . addEventListener ( 'loadedmetadata' , function () { video . play (); }); } äžèšã®åå²ã iOS Safari ãªã©ãHLS åç»ããã€ãã£ããµããŒãããŠãããã©ãŠã¶åãã®åŠçã§ããåçŽã« .m3u8 ãžã® URL ã <video> ã¿ã°ã® src ãžä»äžããŠããã ãã§ããã ïŒãµã³ãã«ã³ãŒãã§ã¯ããããã§ã¹ããã¡ã€ã«ã®ããŒãåŸã«èªååçãããããã«ãªã£ãŠããããã§ãïŒ æ³šæç¹ã»ã€ãŸã¥ãããã€ã³ã hls.js ã¯ã©ã€ã¢ã³ããååŸãã HLS åç»ãã¡ã€ã«çŸ€ã¯ãCORS ãããã§ GET ãªã¯ãšã¹ããèš±å¯ãããç°å¢ã«èšçœ®ããå¿
èŠããããŸã .m3u8 ãããã§ã¹ããã¡ã€ã«ãã¢ããªã® API ãªã©ããè¿åŽããå ŽåãContent-Type ã application/x-mpegURL ã«ããŠæž¡ãå¿
èŠããããŸã iOS Safari ãªã©ã® hls.js é察å¿ãã©ãŠã¶åãã®å®è£
ãæèããå¿
èŠããããŸã hls.js ã«ããå¶åŸ¡ãè€éã«ãªãã±ãŒã¹ã§ã¯ãåããããªå¶åŸ¡ã hls.js é察å¿ãã©ãŠã¶åãã«å®è£
ã§ãããïŒãã€ã¡ãŒãžã§ããªããšææ»ããçºçãããã§ã ãã®ä»ãããã³ããšã³ãã§ã¯ <video> ã¿ã°ã®ãã©ãŠã¶æ¯ã®æåãã衚瀺ã®éãã«æéãããããŸãããïŒããçšåºŠäºæ³ã¯ããŠããŸãããããã¯ãã¡ãã£ã¢ã®åãæ±ãã¯é£ããâŠïŒ hls.js èªäœã¯å°å
¥ãæè»œã§ããµã¯ããš HLS åç»ã®ãã«ããã©ãŠã¶å¯Ÿå¿ãå®çŸã§ãããšãŠã䜿ããããã£ãã§ãã@types ãååšããã®ã§ãTypeScript ç°å¢ã§ãé£ãªãå®è£
ã§ããŸããã SSR ã HLS + AES-128 ã®åçã«ã察å¿ããŠããã®ã§ãèå³ã®ããæ¹ã¯äžåºŠ å
¬åŒããã¥ã¡ã³ã ã確èªããŠã¿ãŠãã ããã ãããã« åŸæ¥ãåç»é
ä¿¡ãµãŒãã¹ãæ§ç¯ããå Žåãffmpeg ãèŒãããšã³ã³ãŒããµãŒãããã¹ããªãŒãã³ã°é
ä¿¡ãµãŒããå¥å»ºãŠããŠãè² è·ã«å¿ããŠã¹ã±ãŒã«ãããŠâŠã®ãããªèšèšãå¿
èŠã ã£ãããšæããŸãã ä»åãMediaConvert ãã¯ãããšãã AWS ãµãŒãã¹ãš hls.js ãå©çšããããšã§ãæè»œã«ãã¹ã±ãŒã©ãã«ãªåç»ãšã³ã³ãŒãïŒHTTP ã¹ããªãŒãã³ã°é
ä¿¡ç°å¢ãæ§ç¯ããããšãã§ããŸããã ãžã§ãã¡ãã¬ãŒã®åç»éžèã¯ãŸã ãªãªãŒã¹ããã°ããã§ãã®ã§ãä»åŸåé¿ãèŠãªããããããªãæ¹åãéããŠãããããšæããŸããæåŸãŸã§ãèªã¿ããã ãããããšãããããŸããã https://www.medley.jp/jobs/
ããã«ã¡ã¯ã第äžéçºã°ã«ãŒãã®ç¢éã§ãããžã§ãã¡ãã¬ãŒéçºãšã³ãžãã¢ãšããŠãäž»ã«ããã¯ãšã³ããæ
åœããŠããŸãã çŽè¿ã§ã¯ããžã§ãã¡ãã¬ãŒãå
æãªãªãŒã¹ãã ãåç»éžèã æ©èœã®éçºãããžã§ã¯ãã«æºãã£ãŠãããåç»ãã¡ã€ã«ã®ã¢ããããŒãïŒé
ä¿¡ç°å¢ã®èšèšã»å®è£
ãè¡ã£ãŠããŸããã ä»åã®ããã°ã§ã¯ããã®ãåç»éžèãæ©èœã®éçºã«å©çšãã AWS Elemental MediaConvert ãµãŒãã¹ãšã hls.js ãšãã OSS ã©ã€ãã©ãªã«ã€ããŠç޹ä»ããããšæããŸãã ãžã§ãã¡ãã¬ãŒã®ãåç»éžèãæ©èœ ã¯ããã«ãä»åãªãªãŒã¹ãããåç»éžèãæ©èœã«ã€ããŠæŠèŠã玹ä»ããŸãã æ°åã³ãããŠã€ã«ã¹æææ¡å€§ã«ãã£ãŠã察é¢ã§ã®é¢æ¥ã«äžå®ãæããããå
Œ
±äº€éæ©é¢ã®å©çšãé£ãããªã£ããããããšã«ãããæºè¶³ãªè»¢è·æŽ»åãã§ããªããªã£ãŠããæ¹ãããã£ãããããšæããŸãã ãã®ãããªèª²é¡ã解決ããããã«ããžã§ãã¡ãã¬ãŒã§ã¯ãªã¢ã«ã¿ã€ã ã«ãªã³ã©ã€ã³ã§é¢æ¥ãè¡ããWEB 颿¥ããšãäºæ¥è
ããããããèšå®ãã質åã«å¯ŸããŠå¿åè
ãåç»ã§åçãéããåç»éžèãã® 2 ã€ã®æ©èœãæäŸéå§ããããŸããã ref. WEB 颿¥ã»åç»éžèæ©èœã®ãªãªãŒã¹ã®ãç¥ãã åç»éžèïŒåç»é¢æ¥ïŒã¯ãè¿å¹Žå¢å åŸåã«ãããªã³ã©ã€ã³éžèã®äžçš®ã§ããäžè¬çã«ãæ±è·è
/ 就掻çã PC ã»ã¹ããŒããã©ã³çã®ã«ã¡ã©ã§ãäºãçšæãããèšåã«å¿ããŠåç»ãæ®åœ±ããäŒæ¥ã«éãããšã§éžèãè¡ããŸãã ref. WEB 颿¥ã»åç»éžèãšã¯ïŒ 宿œã®æµãã䜿çšããŒã«ããããŒã泚æç¹ãªã©ã培åºè§£èª¬ïŒ ç§ãã¡ãžã§ãã¡ãã¬ãŒã®åç»éžèã§ã¯ãäºæ¥æããããããèšå®ãã質åã«å¯ŸããŠãæ±è·è
ãåçåç»ãæåºããããšãã§ããŸããäºæ¥æãæ±è·è
ããåç»ã§è³ªåã»åçãéãããšã§ãæžé¡ã ãã§ã¯äŒãããªãé°å²æ°ã匷ã¿ãçžæã«äŒããããšãã§ããŸãã WEB 颿¥ã»åç»éžèæ©èœã®ãªãªãŒã¹ã®ãç¥ãã åç»é
ä¿¡ãµãŒãã¹ã®èšèšãã€ã³ã Web ã¢ããªã§ãã®ãããªåç»é
ä¿¡ãµãŒãã¹ãéçºããå ŽåãããŠãŒã¶ã«ããåç»ã¢ããããŒãç°å¢ããšããŠãŒã¶ãžã®åç»ã®é
ä¿¡ã»åçç°å¢ããæäŸããå¿
èŠããããŸãã ãžã§ãã¡ãã¬ãŒã§æ±ãåç»ã¯äžè¬å
¬éããããã®ã§ã¯ãªããå
¬éæ¡ä»¶ãè€éã§ãã ãã£ãŠä»åã¯ããã®ãåç»ã¢ããããŒãïŒé
ä¿¡ç°å¢ããèªãµãŒãã¹å
ã«æ§ç¯ããæ¹éããšãã以äžã®ãããªåç»ãŸããã®èšèšãã€ã³ãã«ã€ããŠæ€èšã»æè¡éžå®ãè¡ãããšã«ããŸããã ïŒãã¡ãããèŠä»¶ã«ãã£ãŠã¯ YouTube ããæ³äººåãåç»é
ä¿¡ãã©ãããã©ãŒã ãå¥çŽããæ¹ãæè»œãªå ŽåãããããšæããŸãïŒ åç»ã®é²ç»ã»æ®åœ± ãµããŒããããåç»ãã¡ã€ã«ã®ãã©ãŒããããã©ãããã Web ã¢ããªå
ã«é²ç»æ©èœãèšããã åç»ã®ã¢ããããŒãïŒã¹ãã¬ãŒãžïŒ åç»ãã¡ã€ã«ã®ããªããŒã·ã§ã³ã§ãåç»ãã¡ã€ã«ã®è§£æããè¡ãã åç»ãã¡ã€ã«ã®ã¢ããããŒãå
ïŒã¹ãã¬ãŒãžïŒãã©ãã«ããã åç»ã®ãšã³ã³ãŒã åç»ãã¡ã€ã«ã®ãšã³ã³ãŒã圢åŒïŒH.264ãHLS çïŒãã©ãããã éåæãšã³ã³ãŒãã®å Žåãã¹ããŒã¿ã¹æ€ç¥ã»ãšã©ãŒãã³ããªã³ã°ãã©ãããã åç»ã®é
ä¿¡ïŒããŠã³ããŒãïŒ é
信圢åŒïŒããŠã³ããŒãïŒã¹ããªãŒãã³ã°ïŒãã©ãããã æå·åãããå Žåã埩å·ãã©ã®ããã«è¡ãã åç»ãã¡ã€ã«ã®å
¬éæ¹æ³ïŒã¢ã¯ã»ã¹å¶éïŒãã©ãããã åç»ã®åç Web ããŒãžäžã§åçãããã®ãããã®å Žåã®è¡šç€ºã»åçå¶åŸ¡ãã©ãããã ãã©ãŠã¶ãµããŒããã©ããŸã§ã«ããããé察å¿ã»ãšã©ãŒæã®å¶åŸ¡ãã©ãããã ä»åã¯ãäžèšã®å€ªåã§èšèŒãã ãåç»ã®ãšã³ã³ãŒããã« MediaConvert ãã ãåç»ã®åçãã« hls.js ãããããæ¡çšããŠããŸãã åé
ã®è©³çްã¯çããŸãããå
šäœãéããŠå€§ãŸãã«ã以äžã®ãããŒã§ãåç»ã¢ããããŒã â ãšã³ã³ãŒãïŒå€æïŒâ é
ä¿¡ã»åçããå®çŸããããšã«ããŸããã ãã©ãŠã¶ãã Ajax ã§åç»ã S3 ãžã¢ããããŒããã MediaConvert ãåç»ã HLS 圢åŒã«ãšã³ã³ãŒãïŒå€æïŒãã ãã©ãŠã¶ã§ hls.js ã䜿ãåç»ã CloudFront ããã¹ããªãŒãã³ã°åœ¢åŒã§åä¿¡ãåçãã ä»åã¯ãã®ãåç»ã¢ããããŒã â ãšã³ã³ãŒãïŒå€æïŒâ é
ä¿¡ã»åçãã«çŠç¹ãçµããMediaConvert ãš hls.js ãã©ã®ããã«äœ¿ã£ãã®ãã玹ä»ããŸãã MediaConvert ã«ãã HLS ãšã³ã³ãŒã AWS Elemental MediaConvert ã¯ãS3 ãšã®èŠªåæ§ãé«ããã¡ã€ã«ããŒã¹ã®åç»å€æãµãŒãã¹ã§ããèªåã§ ffmpeg ãªã©ã䜿ã£ãŠåç»ãšã³ã³ãŒããµãŒããæ§ç¯ã»ç®¡çããããšãªããã¹ã±ãŒã©ãã«ãªåç»å€æåŠçãæè»œã«ã·ã¹ãã ã«çµã¿èŸŒãããšãã§ããŸãã ref. AWS Elemental MediaConvert æéã¯åºåããåç»ã®åçæéã«å¿ããåŸé課éã§ããAWS ã³ã³ãœãŒã«ãã GUI ããŒã¹ã§ãšã³ã³ãŒãèšå®ãäœæãããããžã§ãïŒãšã³ã³ãŒãåŠçïŒãç»é²ããããšãã§ããŸãã ãŸããä» AWS ãµãŒãã¹åæ§ã« API ãæäŸãããŠãããAWS CLI ãåèšèªã® SDK ã䜿ã£ãŠããã°ã©ã ãããšã³ã³ãŒãåŠçãç»é²ããããšãã§ããã·ã¹ãã 飿ºã容æã§ãã # CLI ã§ãšã³ã³ãŒããžã§ããç»é²ããäŸ $ aws --endpoint-url https://abcd1234.mediaconvert.region-name-1.amazonaws.com --region region-name-1 mediaconvert create-job --cli-input-json file://~/job.json äžèš CLI ã³ãã³ãã§äžã®ãããªãšã³ã³ãŒãèšå®ãèšèŒãã JSON ã䜿ããžã§ããäœæãããšãS3 äžã®åç»ãã¡ã€ã«ããµã¯ããšãšã³ã³ãŒãããŠãããŸãããžã§ãã¯ãã¥ãŒã€ã³ã°ãããå
éšã§äžŠååŠçãããããã倧éã®ãšã³ã³ãŒãèŠæ±ã«ãç°¡åã«å¿ããããšãã§ããŸãã { ... "Settings" : { "Inputs" : [ { # å
¥åå
ã® S3 ãã±ããäžã®åç»ãã¡ã€ã« key ãæå® "FileInput" : "s3://testcontent/720/example_input_720p.mov" } ], "OutputGroups" : [ { "OutputGroupSettings" : { "FileGroupSettings" : { # åºåå
ã® S3 ãã±ãã key ãæå® "Destination" : "s3://testbucket/output" } }, # åç»ã»é³å£°ã®ãšã³ã³ãŒãèšå®ãæå® # ããã§å質ã¬ãã«æ¯ã«æ¯ãåããè€æ°ã®ãã¡ã€ã«ãåºåããã # ãµã ãã€ã« jpg ãäœæãããããããšãå¯èœ "Outputs" : [ { "VideoDescription" : { ⊠}, "AudioDescriptions" : { ⊠} } ] } ] } } ref. AWSCLI ã䜿çšãã AWSElemental MediaConvertCreateJob ã®äŸ ãšã³ã³ãŒããå®äºãããžã§ãã¯ãcron + SDK ãªã©ã§ API ãä»ããŠå®æãã§ãã¯ããä»ã«ãCloudWatch Events ã«ããã€ãã³ãç£èŠ â Lambda ã§åŠçãããããªããšãã§ããŸãã ref. AWS Elemental MediaConvert ã«ãã CloudWatch ã€ãã³ã ã®äœ¿çš ãªãåç»ãåãšã³ã³ãŒãããã®ã éåžžããŠãŒã¶ããã¢ããããŒããããåç»ãã¡ã€ã«ã¯ãæ¢ã«äœããã®ã³ãŒããã¯ã§å§çž®ãã .mp4 ã .mov ãªã©ã®ã³ã³ãããã©ãŒãããã«å€æãããŠããããšãæ®ã©ã§ãã ããã Web ããŒãžã§ <video> ã¿ã°ã䜿ããããåç»ãã¡ã€ã«ãåçããããšããå Žåã ãåç»ãã©ãŒãããã«ãã©ãŠã¶ãé察å¿ã ãšåçã§ããªãã ãšããç°å¢äŸååé¡ããããŸãã ãã©ãŠã¶ãšåç»ãã©ãŒãããã®ãµããŒã衚 ref. HTML5 video > Browser support ãã®åé¡ã«å¯Ÿå¿ãããããå€ãã®åç»é
ä¿¡ãµãŒãã¹ã§ã¯ããŠãŒã¶ã®åç»ãå€ãã®ç°å¢ã§åçå¯èœãª MP4 ã³ã³ãããã©ãŒãããïŒH.264 + AAC ã³ãŒããã¯ïŒãªã©ã®åœ¢åŒãžãåãšã³ã³ãŒããããŠããŸãã ãžã§ãã¡ãã¬ãŒã®åç»éžèã§ã¯äžèšç®çã«å ããŠãåç»é²èЧæã®åç·ã»ç«¯æ«è² è·ãæãã ãHTTP ã¹ããªãŒãã³ã°åœ¢åŒã ã§åç»ãé
ä¿¡ããããã«ãã¢ããããŒããããåç»ãå
šãŠ HLS åœ¢åŒ ã«ãšã³ã³ãŒãããŠããŸãã HLS - HTTP Live Streaming åœ¢åŒ HLS 㯠HTTP Live Streaming ã®ç¥ã§ãApple 瀟ã®éçºããèŠæ Œã§ããHTTP ããŒã¹ã®ã¹ããªãŒãã³ã°éä¿¡ãããã³ã«ã§ã现åãã«ãã MP4 åç»ãã¡ã€ã«ãåå²ããŠã³ããŒããããããšã§åç»ã®ã¹ããªãŒãã³ã°é
ä¿¡ãå®çŸããŠããŸãã HLS 圢åŒã«ãšã³ã³ãŒããããåç»ã¯ .ts ãšããåå²ãããã¡ãã£ã¢ãã¡ã€ã«çŸ€ãšã .m3u8 ãšãããã¡ãã£ã¢ãã¡ã€ã«ã®ååŸå
ãç§æ°ãªã©ãèšèŒããããã¹ããã¡ã€ã«ã§æ§æãããŸãã .m3u8 ãã¡ã€ã«ã®äŸïŒãããã§ã¹ããã¡ã€ã«ããã¬ã€ãªã¹ããã¡ã€ã«ãšãïŒ #EXTM3U #EXT-X-TARGETDURATION:10 #EXT-X-VERSION:3 #EXT-X-MEDIA-SEQUENCE:0 #EXT-X-PLAYLIST-TYPE:VOD #EXTINF:9.97663, media-0.ts #EXTINF:9.97663, media-1.ts #EXTINF:7.10710, media-2.ts #EXT-X-ENDLIST ref. RFC 8216: HTTP Live Streaming HLS ã¯ä»ã®ã¹ããªãŒãã³ã°åœ¢åŒãšæ¯èŒããŠãã©ã€ãé
ä¿¡ / VOD ã©ã¡ãã«ã察å¿å¯èœãªããšã察å¿ãã©ãŠã¶ãå€ãããšãå°çšã®é
ä¿¡ãµãŒãã䜿ããã«é
ä¿¡å¯èœãªããšãªã©ãããè¿å¹Žã®åç»é
ä¿¡ãµãŒãã¹ã§åºãå©çšãããŠããŸãã Web ãšã³ãžãã¢ã®èŠç¹ããèŠãŠãã HTTP ããŒã¹ãªãããã£ãã·ã¥ã HTTPS æå·åãªã©ãæ¢å Web æè¡ãšæãåãããããšãæ³åãããããæ±ããããå°è±¡ã§ããã MediaConvert ã® HLS ãšã³ã³ãŒããžã§ãèšå® å®éã«ããã°ã©ã ãã API çµç±ã§ HLS ãšã³ã³ãŒããžã§ããç»é²ããéã®èšå® JSON ã¯ã以äžã®ããã« GUI ã§ãžã§ããã³ãã¬ãŒããäœæããŠç¢ºèªããããšãã§ããŸãã ãã®ã JSON ã衚瀺ãã§ãåè¿°ãã CLI ã³ãã³ã mediaconvert create-job --cli-input-json ã«æž¡ãã JSON ã衚瀺ãããŸããå®è£
ã®éã«ã¯ãã¡ããåèã«ããªããã ãŠãŒã¶ãŒã¬ã€ã ãåç
§ããŠå©çšãããæ©èœã«ããããèšå®ã远å ããŠããããšãããããããŸãã æ³šæç¹ã»ã€ãŸã¥ãããã€ã³ã å©çšåã« IAM ã§ MediaConvert çšããŒã«ã®èšå®ãå¿
èŠã§ã ã¹ããã 3. IAM æš©éã®èšå® AWS ã³ã³ãœãŒã«ã® Service Quotas > AWS ãµãŒãã¹ > AWS Elemental MediaConvert ãã確èªã§ããŸããããšã³ã³ãŒã䞊è¡åŠçã®åæå®è¡æ°äžé㯠20 ã«ãªã£ãŠããŸã AWS ã«ãŒãã¢ã«ãŠã³ã 1 ã€ã«ã€ã 1 ãµãŒãã¹ãå²åœãŠãããã®ã§ããããå¢ããããå Žåã¯ç³è«ãå¿
èŠã§ã ãšã³ã³ãŒããžã§ãããã¥ãŒã€ã³ã°ããããã¥ãŒããäœæããŠããžã§ãã®ç»é²æã«éžã¹ãã®ã§ãããäžèšããã䞊è¡åŠçã®åæå®è¡æ°äžéãã¯ãã®ããã¥ãŒãæ¯ã«åçã«æ¯ãåããããŸã äŸãã°ãæ¬çªãã¥ãŒããšãæ€èšŒãã¥ãŒãã® 2 ã€ã®ãã¥ãŒãäœæããå Žåãããããã®äžŠè¡åŠçã®åæå®è¡æ°äžé㯠10 ãã€ã«ãªãã®ã§æ³šæããŠãã ãã ãããã§ã¹ãæé圢åŒïŒManifest duration formatïŒã«æŽæ°ïŒINTEGERïŒãæå®ããŠãããšãiOS Safari ã§ãåç»ã®å®éã®åçæéãšãåçãã¬ã€ã€ãŒã®ã·ãŒã¯ããŒã«è¡šç€ºãããåèšæéã«ãºã¬ãçãããåé¡ããããŸãã æµ®åå°æ°ç¹ïŒFLOATING POINTïŒã«å€æŽããããšã§å¯Ÿå¿ããŸããããããã§ã¹ããã¡ã€ã«ã«åºåãããå .ts ãã¡ã€ã«ã®é·ãããæµ®åå°æ°ç¹ â æŽæ°ã«å€æããåãäžããããããšã§ãºã¬ãçããŠããããã§ãã hls.js ã«ãã HLS åç»ã®åçå¶åŸ¡ MediaConvert ã«ãã HLS 圢åŒã«ãšã³ã³ãŒããããåç»ããWeb ãã©ãŠã¶ã§åçããããã«å¿
èŠãªã®ããhls.js ã§ãã ref. video-dev/hls.js å®ã¯ HLS ã«ããã¹ããªãŒãã³ã°é
ä¿¡ã¯ãçŸç¶ Safari ãªã©éããããã©ãŠã¶ã§ãããã€ãã£ãã§ãµããŒããããŠããŸããã ref. https://caniuse.com/http-live-streaming ãã® HLS åç»ã Safari 以å€ã® Google Chrome ã IE11 ãªã©ã®äž»èŠãã©ãŠã¶ã§åçå¯èœã«ãããããhls.js ãå©çšãããŠããŸããå
éšçã«ã¯ãé察å¿ãã©ãŠã¶ç°å¢ã«ãããŠããã©ãŠã¶ã® MediaSource æ¡åŒµ ã䜿ã£ãŠ HLS åç»ãåçãã仿§ã«ãªã£ãŠããŸãã Video.js ãšã®æ¯èŒ 䌌ããããªã©ã€ãã©ãªã« Video.js ãšãããã®ããããå°å
¥ãè¿·ã£ãã®ã§ãã ⊠Video.js 㯠UI ãã»ããã«ãªã£ãã HLS ã«å¯Ÿå¿ããåçãã¬ã€ã€ãŒãã©ã€ãã©ãª HLS 察å¿ä»¥å€ã«ããåå¹ãç« åããªã©æ©èœãè±å¯ hls.js ã¯ãã©ãŠã¶æšæºã® <video> ã¿ã°ã§ HLS ã«å¯Ÿå¿ããããšã ããç®çã«ããã HLS ã¯ã©ã€ã¢ã³ããã©ã€ãã©ãª UI ãªã©ã¯ãªããåç»åçãã¬ã€ã€ãŒã¯ãã©ãŠã¶æšæºã®ãŸãŸ âŠãšãäžèšã®ããã« hls.js ã®æ¹ãã·ã³ãã«ã«ããããããšãå®çŸã§ãããããä»å㯠hls.js ãæ¡çšããŸããã GitHub ã®ã¹ã¿ãŒæ°ã¯å
çºã® Video.js ã®æ¹ãå€ãã®ã§ãããhls.js ãéçºã¯æŽ»çºã§ãæ¥æ¬ã§ã¯ ã°ãã·ãŒ ãããäžççã«ã¯ TED ã Twitter ã§ãæ¡çšãããŠãããååå®çžŸãããããšæããŸãã hls.js ã«ããå®è£
åºæ¬çã«ã¯ README ã® Getting Started ã®éãã§å®è£
ã§ããŸããäžéš README ã®ãµã³ãã«ã³ãŒãããæç²ããŠè§£èª¬ãããšâŠ var video = document . getElementById ( "video" ); var videoSrc = "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8" ; if ( Hls . isSupported ()) { var hls = new Hls (); hls . loadSource ( videoSrc ); hls . attachMedia ( video ); hls . on ( Hls . Events . MANIFEST_PARSED , function () { video . play (); }); } äžèš Hls.isSupported() ã®åå²ã§ãHLS ããã€ãã£ããµããŒãããŠããªããã©ãŠã¶ã®åŠçãå®è£
ããŠããŸãã æ¬æ¥ <video> ã® src 屿§ã«ã»ããããã¹ã .m3u8 ãã¡ã€ã«ã® URL ãž hls.loadSource() ã§ã¢ã¯ã»ã¹ãããã¯ã©ã€ã¢ã³ããã XHR ãªã¯ãšã¹ããé£ã°ããŸãããã®åŸ hls.attachMedia() ã§ã€ã³ã¹ã¿ã³ã¹ã DOM äžã® <video> ã¿ã°ã«çŽã¥ããŠããŸãã else if ( video . canPlayType ( 'application/vnd.apple.mpegurl' )) { video . src = videoSrc ; video . addEventListener ( 'loadedmetadata' , function () { video . play (); }); } äžèšã®åå²ã iOS Safari ãªã©ãHLS åç»ããã€ãã£ããµããŒãããŠãããã©ãŠã¶åãã®åŠçã§ããåçŽã« .m3u8 ãžã® URL ã <video> ã¿ã°ã® src ãžä»äžããŠããã ãã§ããã ïŒãµã³ãã«ã³ãŒãã§ã¯ããããã§ã¹ããã¡ã€ã«ã®ããŒãåŸã«èªååçãããããã«ãªã£ãŠããããã§ãïŒ æ³šæç¹ã»ã€ãŸã¥ãããã€ã³ã hls.js ã¯ã©ã€ã¢ã³ããååŸãã HLS åç»ãã¡ã€ã«çŸ€ã¯ãCORS ãããã§ GET ãªã¯ãšã¹ããèš±å¯ãããç°å¢ã«èšçœ®ããå¿
èŠããããŸã .m3u8 ãããã§ã¹ããã¡ã€ã«ãã¢ããªã® API ãªã©ããè¿åŽããå ŽåãContent-Type ã application/x-mpegURL ã«ããŠæž¡ãå¿
èŠããããŸã iOS Safari ãªã©ã® hls.js é察å¿ãã©ãŠã¶åãã®å®è£
ãæèããå¿
èŠããããŸã hls.js ã«ããå¶åŸ¡ãè€éã«ãªãã±ãŒã¹ã§ã¯ãåããããªå¶åŸ¡ã hls.js é察å¿ãã©ãŠã¶åãã«å®è£
ã§ãããïŒãã€ã¡ãŒãžã§ããªããšææ»ããçºçãããã§ã ãã®ä»ãããã³ããšã³ãã§ã¯ <video> ã¿ã°ã®ãã©ãŠã¶æ¯ã®æåãã衚瀺ã®éãã«æéãããããŸãããïŒããçšåºŠäºæ³ã¯ããŠããŸãããããã¯ãã¡ãã£ã¢ã®åãæ±ãã¯é£ããâŠïŒ hls.js èªäœã¯å°å
¥ãæè»œã§ããµã¯ããš HLS åç»ã®ãã«ããã©ãŠã¶å¯Ÿå¿ãå®çŸã§ãããšãŠã䜿ããããã£ãã§ãã@types ãååšããã®ã§ãTypeScript ç°å¢ã§ãé£ãªãå®è£
ã§ããŸããã SSR ã HLS + AES-128 ã®åçã«ã察å¿ããŠããã®ã§ãèå³ã®ããæ¹ã¯äžåºŠ å
¬åŒããã¥ã¡ã³ã ã確èªããŠã¿ãŠãã ããã ãããã« åŸæ¥ãåç»é
ä¿¡ãµãŒãã¹ãæ§ç¯ããå Žåãffmpeg ãèŒãããšã³ã³ãŒããµãŒãããã¹ããªãŒãã³ã°é
ä¿¡ãµãŒããå¥å»ºãŠããŠãè² è·ã«å¿ããŠã¹ã±ãŒã«ãããŠâŠã®ãããªèšèšãå¿
èŠã ã£ãããšæããŸãã ä»åãMediaConvert ãã¯ãããšãã AWS ãµãŒãã¹ãš hls.js ãå©çšããããšã§ãæè»œã«ãã¹ã±ãŒã©ãã«ãªåç»ãšã³ã³ãŒãïŒHTTP ã¹ããªãŒãã³ã°é
ä¿¡ç°å¢ãæ§ç¯ããããšãã§ããŸããã ãžã§ãã¡ãã¬ãŒã®åç»éžèã¯ãŸã ãªãªãŒã¹ããã°ããã§ãã®ã§ãä»åŸåé¿ãèŠãªããããããªãæ¹åãéããŠãããããšæããŸããæåŸãŸã§ãèªã¿ããã ãããããšãããããŸããã åéã®äžèЧ | æ ªåŒäŒç€Ÿã¡ãã¬ãŒ ã¡ãã¬ãŒã®æ¡çšæ
å ±ã¯ãã¡ãããã確èªãã ããã www.medley.jp
ããã«ã¡ã¯ã第äžéçºã°ã«ãŒãã®ç¢éã§ãããžã§ãã¡ãã¬ãŒéçºãšã³ãžãã¢ãšããŠãäž»ã«ããã¯ãšã³ããæ
åœããŠããŸãã çŽè¿ã§ã¯ããžã§ãã¡ãã¬ãŒãå
æãªãªãŒã¹ãã ãåç»éžèã æ©èœã®éçºãããžã§ã¯ãã«æºãã£ãŠãããåç»ãã¡ã€ã«ã®ã¢ããããŒãïŒé
ä¿¡ç°å¢ã®èšèšã»å®è£
ãè¡ã£ãŠããŸããã ä»åã®ããã°ã§ã¯ããã®ãåç»éžèãæ©èœã®éçºã«å©çšãã AWS Elemental MediaConvert ãµãŒãã¹ãšã hls.js ãšãã OSS ã©ã€ãã©ãªã«ã€ããŠç޹ä»ããããšæããŸãã ãžã§ãã¡ãã¬ãŒã®ãåç»éžèãæ©èœ ã¯ããã«ãä»åãªãªãŒã¹ãããåç»éžèãæ©èœã«ã€ããŠæŠèŠã玹ä»ããŸãã æ°åã³ãããŠã€ã«ã¹æææ¡å€§ã«ãã£ãŠã察é¢ã§ã®é¢æ¥ã«äžå®ãæããããå
Œ
±äº€éæ©é¢ã®å©çšãé£ãããªã£ããããããšã«ãããæºè¶³ãªè»¢è·æŽ»åãã§ããªããªã£ãŠããæ¹ãããã£ãããããšæããŸãã ãã®ãããªèª²é¡ã解決ããããã«ããžã§ãã¡ãã¬ãŒã§ã¯ãªã¢ã«ã¿ã€ã ã«ãªã³ã©ã€ã³ã§é¢æ¥ãè¡ããWEB 颿¥ããšãäºæ¥è
ããããããèšå®ãã質åã«å¯ŸããŠå¿åè
ãåç»ã§åçãéããåç»éžèãã® 2 ã€ã®æ©èœãæäŸéå§ããããŸããã ref. WEB 颿¥ã»åç»éžèæ©èœã®ãªãªãŒã¹ã®ãç¥ãã åç»éžèïŒåç»é¢æ¥ïŒã¯ãè¿å¹Žå¢å åŸåã«ãããªã³ã©ã€ã³éžèã®äžçš®ã§ããäžè¬çã«ãæ±è·è
/ 就掻çã PC ã»ã¹ããŒããã©ã³çã®ã«ã¡ã©ã§ãäºãçšæãããèšåã«å¿ããŠåç»ãæ®åœ±ããäŒæ¥ã«éãããšã§éžèãè¡ããŸãã ref. WEB 颿¥ã»åç»éžèãšã¯ïŒ 宿œã®æµãã䜿çšããŒã«ããããŒã泚æç¹ãªã©ã培åºè§£èª¬ïŒ ç§ãã¡ãžã§ãã¡ãã¬ãŒã®åç»éžèã§ã¯ãäºæ¥æããããããèšå®ãã質åã«å¯ŸããŠãæ±è·è
ãåçåç»ãæåºããããšãã§ããŸããäºæ¥æãæ±è·è
ããåç»ã§è³ªåã»åçãéãããšã§ãæžé¡ã ãã§ã¯äŒãããªãé°å²æ°ã匷ã¿ãçžæã«äŒããããšãã§ããŸãã WEB 颿¥ã»åç»éžèæ©èœã®ãªãªãŒã¹ã®ãç¥ãã åç»é
ä¿¡ãµãŒãã¹ã®èšèšãã€ã³ã Web ã¢ããªã§ãã®ãããªåç»é
ä¿¡ãµãŒãã¹ãéçºããå ŽåãããŠãŒã¶ã«ããåç»ã¢ããããŒãç°å¢ããšããŠãŒã¶ãžã®åç»ã®é
ä¿¡ã»åçç°å¢ããæäŸããå¿
èŠããããŸãã ãžã§ãã¡ãã¬ãŒã§æ±ãåç»ã¯äžè¬å
¬éããããã®ã§ã¯ãªããå
¬éæ¡ä»¶ãè€éã§ãã ãã£ãŠä»åã¯ããã®ãåç»ã¢ããããŒãïŒé
ä¿¡ç°å¢ããèªãµãŒãã¹å
ã«æ§ç¯ããæ¹éããšãã以äžã®ãããªåç»ãŸããã®èšèšãã€ã³ãã«ã€ããŠæ€èšã»æè¡éžå®ãè¡ãããšã«ããŸããã ïŒãã¡ãããèŠä»¶ã«ãã£ãŠã¯ YouTube ããæ³äººåãåç»é
ä¿¡ãã©ãããã©ãŒã ãå¥çŽããæ¹ãæè»œãªå ŽåãããããšæããŸãïŒ åç»ã®é²ç»ã»æ®åœ± ãµããŒããããåç»ãã¡ã€ã«ã®ãã©ãŒããããã©ãããã Web ã¢ããªå
ã«é²ç»æ©èœãèšããã åç»ã®ã¢ããããŒãïŒã¹ãã¬ãŒãžïŒ åç»ãã¡ã€ã«ã®ããªããŒã·ã§ã³ã§ãåç»ãã¡ã€ã«ã®è§£æããè¡ãã åç»ãã¡ã€ã«ã®ã¢ããããŒãå
ïŒã¹ãã¬ãŒãžïŒãã©ãã«ããã åç»ã®ãšã³ã³ãŒã åç»ãã¡ã€ã«ã®ãšã³ã³ãŒã圢åŒïŒH.264ãHLS çïŒãã©ãããã éåæãšã³ã³ãŒãã®å Žåãã¹ããŒã¿ã¹æ€ç¥ã»ãšã©ãŒãã³ããªã³ã°ãã©ãããã åç»ã®é
ä¿¡ïŒããŠã³ããŒãïŒ é
信圢åŒïŒããŠã³ããŒãïŒã¹ããªãŒãã³ã°ïŒãã©ãããã æå·åãããå Žåã埩å·ãã©ã®ããã«è¡ãã åç»ãã¡ã€ã«ã®å
¬éæ¹æ³ïŒã¢ã¯ã»ã¹å¶éïŒãã©ãããã åç»ã®åç Web ããŒãžäžã§åçãããã®ãããã®å Žåã®è¡šç€ºã»åçå¶åŸ¡ãã©ãããã ãã©ãŠã¶ãµããŒããã©ããŸã§ã«ããããé察å¿ã»ãšã©ãŒæã®å¶åŸ¡ãã©ãããã ä»åã¯ãäžèšã®å€ªåã§èšèŒãã ãåç»ã®ãšã³ã³ãŒããã« MediaConvert ãã ãåç»ã®åçãã« hls.js ãããããæ¡çšããŠããŸãã åé
ã®è©³çްã¯çããŸãããå
šäœãéããŠå€§ãŸãã«ã以äžã®ãããŒã§ãåç»ã¢ããããŒã â ãšã³ã³ãŒãïŒå€æïŒâ é
ä¿¡ã»åçããå®çŸããããšã«ããŸããã ãã©ãŠã¶ãã Ajax ã§åç»ã S3 ãžã¢ããããŒããã MediaConvert ãåç»ã HLS 圢åŒã«ãšã³ã³ãŒãïŒå€æïŒãã ãã©ãŠã¶ã§ hls.js ã䜿ãåç»ã CloudFront ããã¹ããªãŒãã³ã°åœ¢åŒã§åä¿¡ãåçãã ä»åã¯ãã®ãåç»ã¢ããããŒã â ãšã³ã³ãŒãïŒå€æïŒâ é
ä¿¡ã»åçãã«çŠç¹ãçµããMediaConvert ãš hls.js ãã©ã®ããã«äœ¿ã£ãã®ãã玹ä»ããŸãã MediaConvert ã«ãã HLS ãšã³ã³ãŒã AWS Elemental MediaConvert ã¯ãS3 ãšã®èŠªåæ§ãé«ããã¡ã€ã«ããŒã¹ã®åç»å€æãµãŒãã¹ã§ããèªåã§ ffmpeg ãªã©ã䜿ã£ãŠåç»ãšã³ã³ãŒããµãŒããæ§ç¯ã»ç®¡çããããšãªããã¹ã±ãŒã©ãã«ãªåç»å€æåŠçãæè»œã«ã·ã¹ãã ã«çµã¿èŸŒãããšãã§ããŸãã ref. AWS Elemental MediaConvert æéã¯åºåããåç»ã®åçæéã«å¿ããåŸé課éã§ããAWS ã³ã³ãœãŒã«ãã GUI ããŒã¹ã§ãšã³ã³ãŒãèšå®ãäœæãããããžã§ãïŒãšã³ã³ãŒãåŠçïŒãç»é²ããããšãã§ããŸãã ãŸããä» AWS ãµãŒãã¹åæ§ã« API ãæäŸãããŠãããAWS CLI ãåèšèªã® SDK ã䜿ã£ãŠããã°ã©ã ãããšã³ã³ãŒãåŠçãç»é²ããããšãã§ããã·ã¹ãã 飿ºã容æã§ãã # CLI ã§ãšã³ã³ãŒããžã§ããç»é²ããäŸ $ aws --endpoint-url https://abcd1234.mediaconvert.region-name-1.amazonaws.com --region region-name-1 mediaconvert create-job --cli-input-json file://~/job.json äžèš CLI ã³ãã³ãã§äžã®ãããªãšã³ã³ãŒãèšå®ãèšèŒãã JSON ã䜿ããžã§ããäœæãããšãS3 äžã®åç»ãã¡ã€ã«ããµã¯ããšãšã³ã³ãŒãããŠãããŸãããžã§ãã¯ãã¥ãŒã€ã³ã°ãããå
éšã§äžŠååŠçãããããã倧éã®ãšã³ã³ãŒãèŠæ±ã«ãç°¡åã«å¿ããããšãã§ããŸãã { ... "Settings" : { "Inputs" : [ { # å
¥åå
ã® S3 ãã±ããäžã®åç»ãã¡ã€ã« key ãæå® "FileInput" : "s3://testcontent/720/example_input_720p.mov" } ], "OutputGroups" : [ { "OutputGroupSettings" : { "FileGroupSettings" : { # åºåå
ã® S3 ãã±ãã key ãæå® "Destination" : "s3://testbucket/output" } }, # åç»ã»é³å£°ã®ãšã³ã³ãŒãèšå®ãæå® # ããã§å質ã¬ãã«æ¯ã«æ¯ãåããè€æ°ã®ãã¡ã€ã«ãåºåããã # ãµã ãã€ã« jpg ãäœæãããããããšãå¯èœ "Outputs" : [ { "VideoDescription" : { ⊠}, "AudioDescriptions" : { ⊠} } ] } ] } } ref. AWSCLI ã䜿çšãã AWSElemental MediaConvertCreateJob ã®äŸ ãšã³ã³ãŒããå®äºãããžã§ãã¯ãcron + SDK ãªã©ã§ API ãä»ããŠå®æãã§ãã¯ããä»ã«ãCloudWatch Events ã«ããã€ãã³ãç£èŠ â Lambda ã§åŠçãããããªããšãã§ããŸãã ref. AWS Elemental MediaConvert ã«ãã CloudWatch ã€ãã³ã ã®äœ¿çš ãªãåç»ãåãšã³ã³ãŒãããã®ã éåžžããŠãŒã¶ããã¢ããããŒããããåç»ãã¡ã€ã«ã¯ãæ¢ã«äœããã®ã³ãŒããã¯ã§å§çž®ãã .mp4 ã .mov ãªã©ã®ã³ã³ãããã©ãŒãããã«å€æãããŠããããšãæ®ã©ã§ãã ããã Web ããŒãžã§ <video> ã¿ã°ã䜿ããããåç»ãã¡ã€ã«ãåçããããšããå Žåã ãåç»ãã©ãŒãããã«ãã©ãŠã¶ãé察å¿ã ãšåçã§ããªãã ãšããç°å¢äŸååé¡ããããŸãã ãã©ãŠã¶ãšåç»ãã©ãŒãããã®ãµããŒã衚 ref. HTML5 video > Browser support ãã®åé¡ã«å¯Ÿå¿ãããããå€ãã®åç»é
ä¿¡ãµãŒãã¹ã§ã¯ããŠãŒã¶ã®åç»ãå€ãã®ç°å¢ã§åçå¯èœãª MP4 ã³ã³ãããã©ãŒãããïŒH.264 + AAC ã³ãŒããã¯ïŒãªã©ã®åœ¢åŒãžãåãšã³ã³ãŒããããŠããŸãã ãžã§ãã¡ãã¬ãŒã®åç»éžèã§ã¯äžèšç®çã«å ããŠãåç»é²èЧæã®åç·ã»ç«¯æ«è² è·ãæãã ãHTTP ã¹ããªãŒãã³ã°åœ¢åŒã ã§åç»ãé
ä¿¡ããããã«ãã¢ããããŒããããåç»ãå
šãŠ HLS åœ¢åŒ ã«ãšã³ã³ãŒãããŠããŸãã HLS - HTTP Live Streaming åœ¢åŒ HLS 㯠HTTP Live Streaming ã®ç¥ã§ãApple 瀟ã®éçºããèŠæ Œã§ããHTTP ããŒã¹ã®ã¹ããªãŒãã³ã°éä¿¡ãããã³ã«ã§ã现åãã«ãã MP4 åç»ãã¡ã€ã«ãåå²ããŠã³ããŒããããããšã§åç»ã®ã¹ããªãŒãã³ã°é
ä¿¡ãå®çŸããŠããŸãã HLS 圢åŒã«ãšã³ã³ãŒããããåç»ã¯ .ts ãšããåå²ãããã¡ãã£ã¢ãã¡ã€ã«çŸ€ãšã .m3u8 ãšãããã¡ãã£ã¢ãã¡ã€ã«ã®ååŸå
ãç§æ°ãªã©ãèšèŒããããã¹ããã¡ã€ã«ã§æ§æãããŸãã .m3u8 ãã¡ã€ã«ã®äŸïŒãããã§ã¹ããã¡ã€ã«ããã¬ã€ãªã¹ããã¡ã€ã«ãšãïŒ #EXTM3U #EXT-X-TARGETDURATION:10 #EXT-X-VERSION:3 #EXT-X-MEDIA-SEQUENCE:0 #EXT-X-PLAYLIST-TYPE:VOD #EXTINF:9.97663, media-0.ts #EXTINF:9.97663, media-1.ts #EXTINF:7.10710, media-2.ts #EXT-X-ENDLIST ref. RFC 8216: HTTP Live Streaming HLS ã¯ä»ã®ã¹ããªãŒãã³ã°åœ¢åŒãšæ¯èŒããŠãã©ã€ãé
ä¿¡ / VOD ã©ã¡ãã«ã察å¿å¯èœãªããšã察å¿ãã©ãŠã¶ãå€ãããšãå°çšã®é
ä¿¡ãµãŒãã䜿ããã«é
ä¿¡å¯èœãªããšãªã©ãããè¿å¹Žã®åç»é
ä¿¡ãµãŒãã¹ã§åºãå©çšãããŠããŸãã Web ãšã³ãžãã¢ã®èŠç¹ããèŠãŠãã HTTP ããŒã¹ãªãããã£ãã·ã¥ã HTTPS æå·åãªã©ãæ¢å Web æè¡ãšæãåãããããšãæ³åãããããæ±ããããå°è±¡ã§ããã MediaConvert ã® HLS ãšã³ã³ãŒããžã§ãèšå® å®éã«ããã°ã©ã ãã API çµç±ã§ HLS ãšã³ã³ãŒããžã§ããç»é²ããéã®èšå® JSON ã¯ã以äžã®ããã« GUI ã§ãžã§ããã³ãã¬ãŒããäœæããŠç¢ºèªããããšãã§ããŸãã ãã®ã JSON ã衚瀺ãã§ãåè¿°ãã CLI ã³ãã³ã mediaconvert create-job --cli-input-json ã«æž¡ãã JSON ã衚瀺ãããŸããå®è£
ã®éã«ã¯ãã¡ããåèã«ããªããã ãŠãŒã¶ãŒã¬ã€ã ãåç
§ããŠå©çšãããæ©èœã«ããããèšå®ã远å ããŠããããšãããããããŸãã æ³šæç¹ã»ã€ãŸã¥ãããã€ã³ã å©çšåã« IAM ã§ MediaConvert çšããŒã«ã®èšå®ãå¿
èŠã§ã ã¹ããã 3. IAM æš©éã®èšå® AWS ã³ã³ãœãŒã«ã® Service Quotas > AWS ãµãŒãã¹ > AWS Elemental MediaConvert ãã確èªã§ããŸããããšã³ã³ãŒã䞊è¡åŠçã®åæå®è¡æ°äžé㯠20 ã«ãªã£ãŠããŸã AWS ã«ãŒãã¢ã«ãŠã³ã 1 ã€ã«ã€ã 1 ãµãŒãã¹ãå²åœãŠãããã®ã§ããããå¢ããããå Žåã¯ç³è«ãå¿
èŠã§ã ãšã³ã³ãŒããžã§ãããã¥ãŒã€ã³ã°ããããã¥ãŒããäœæããŠããžã§ãã®ç»é²æã«éžã¹ãã®ã§ãããäžèšããã䞊è¡åŠçã®åæå®è¡æ°äžéãã¯ãã®ããã¥ãŒãæ¯ã«åçã«æ¯ãåããããŸã äŸãã°ãæ¬çªãã¥ãŒããšãæ€èšŒãã¥ãŒãã® 2 ã€ã®ãã¥ãŒãäœæããå Žåãããããã®äžŠè¡åŠçã®åæå®è¡æ°äžé㯠10 ãã€ã«ãªãã®ã§æ³šæããŠãã ãã ãããã§ã¹ãæé圢åŒïŒManifest duration formatïŒã«æŽæ°ïŒINTEGERïŒãæå®ããŠãããšãiOS Safari ã§ãåç»ã®å®éã®åçæéãšãåçãã¬ã€ã€ãŒã®ã·ãŒã¯ããŒã«è¡šç€ºãããåèšæéã«ãºã¬ãçãããåé¡ããããŸãã æµ®åå°æ°ç¹ïŒFLOATING POINTïŒã«å€æŽããããšã§å¯Ÿå¿ããŸããããããã§ã¹ããã¡ã€ã«ã«åºåãããå .ts ãã¡ã€ã«ã®é·ãããæµ®åå°æ°ç¹ â æŽæ°ã«å€æããåãäžããããããšã§ãºã¬ãçããŠããããã§ãã hls.js ã«ãã HLS åç»ã®åçå¶åŸ¡ MediaConvert ã«ãã HLS 圢åŒã«ãšã³ã³ãŒããããåç»ããWeb ãã©ãŠã¶ã§åçããããã«å¿
èŠãªã®ããhls.js ã§ãã ref. video-dev/hls.js å®ã¯ HLS ã«ããã¹ããªãŒãã³ã°é
ä¿¡ã¯ãçŸç¶ Safari ãªã©éããããã©ãŠã¶ã§ãããã€ãã£ãã§ãµããŒããããŠããŸããã ref. https://caniuse.com/http-live-streaming ãã® HLS åç»ã Safari 以å€ã® Google Chrome ã IE11 ãªã©ã®äž»èŠãã©ãŠã¶ã§åçå¯èœã«ãããããhls.js ãå©çšãããŠããŸããå
éšçã«ã¯ãé察å¿ãã©ãŠã¶ç°å¢ã«ãããŠããã©ãŠã¶ã® MediaSource æ¡åŒµ ã䜿ã£ãŠ HLS åç»ãåçãã仿§ã«ãªã£ãŠããŸãã Video.js ãšã®æ¯èŒ 䌌ããããªã©ã€ãã©ãªã« Video.js ãšãããã®ããããå°å
¥ãè¿·ã£ãã®ã§ãã ⊠Video.js 㯠UI ãã»ããã«ãªã£ãã HLS ã«å¯Ÿå¿ããåçãã¬ã€ã€ãŒãã©ã€ãã©ãª HLS 察å¿ä»¥å€ã«ããåå¹ãç« åããªã©æ©èœãè±å¯ hls.js ã¯ãã©ãŠã¶æšæºã® <video> ã¿ã°ã§ HLS ã«å¯Ÿå¿ããããšã ããç®çã«ããã HLS ã¯ã©ã€ã¢ã³ããã©ã€ãã©ãª UI ãªã©ã¯ãªããåç»åçãã¬ã€ã€ãŒã¯ãã©ãŠã¶æšæºã®ãŸãŸ âŠãšãäžèšã®ããã« hls.js ã®æ¹ãã·ã³ãã«ã«ããããããšãå®çŸã§ãããããä»å㯠hls.js ãæ¡çšããŸããã GitHub ã®ã¹ã¿ãŒæ°ã¯å
çºã® Video.js ã®æ¹ãå€ãã®ã§ãããhls.js ãéçºã¯æŽ»çºã§ãæ¥æ¬ã§ã¯ ã°ãã·ãŒ ãããäžççã«ã¯ TED ã Twitter ã§ãæ¡çšãããŠãããååå®çžŸãããããšæããŸãã hls.js ã«ããå®è£
åºæ¬çã«ã¯ README ã® Getting Started ã®éãã§å®è£
ã§ããŸããäžéš README ã®ãµã³ãã«ã³ãŒãããæç²ããŠè§£èª¬ãããšâŠ var video = document . getElementById ( "video" ); var videoSrc = "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8" ; if ( Hls . isSupported ()) { var hls = new Hls (); hls . loadSource ( videoSrc ); hls . attachMedia ( video ); hls . on ( Hls . Events . MANIFEST_PARSED , function () { video . play (); }); } äžèš Hls.isSupported() ã®åå²ã§ãHLS ããã€ãã£ããµããŒãããŠããªããã©ãŠã¶ã®åŠçãå®è£
ããŠããŸãã æ¬æ¥ <video> ã® src 屿§ã«ã»ããããã¹ã .m3u8 ãã¡ã€ã«ã® URL ãž hls.loadSource() ã§ã¢ã¯ã»ã¹ãããã¯ã©ã€ã¢ã³ããã XHR ãªã¯ãšã¹ããé£ã°ããŸãããã®åŸ hls.attachMedia() ã§ã€ã³ã¹ã¿ã³ã¹ã DOM äžã® <video> ã¿ã°ã«çŽã¥ããŠããŸãã else if ( video . canPlayType ( 'application/vnd.apple.mpegurl' )) { video . src = videoSrc ; video . addEventListener ( 'loadedmetadata' , function () { video . play (); }); } äžèšã®åå²ã iOS Safari ãªã©ãHLS åç»ããã€ãã£ããµããŒãããŠãããã©ãŠã¶åãã®åŠçã§ããåçŽã« .m3u8 ãžã® URL ã <video> ã¿ã°ã® src ãžä»äžããŠããã ãã§ããã ïŒãµã³ãã«ã³ãŒãã§ã¯ããããã§ã¹ããã¡ã€ã«ã®ããŒãåŸã«èªååçãããããã«ãªã£ãŠããããã§ãïŒ æ³šæç¹ã»ã€ãŸã¥ãããã€ã³ã hls.js ã¯ã©ã€ã¢ã³ããååŸãã HLS åç»ãã¡ã€ã«çŸ€ã¯ãCORS ãããã§ GET ãªã¯ãšã¹ããèš±å¯ãããç°å¢ã«èšçœ®ããå¿
èŠããããŸã .m3u8 ãããã§ã¹ããã¡ã€ã«ãã¢ããªã® API ãªã©ããè¿åŽããå ŽåãContent-Type ã application/x-mpegURL ã«ããŠæž¡ãå¿
èŠããããŸã iOS Safari ãªã©ã® hls.js é察å¿ãã©ãŠã¶åãã®å®è£
ãæèããå¿
èŠããããŸã hls.js ã«ããå¶åŸ¡ãè€éã«ãªãã±ãŒã¹ã§ã¯ãåããããªå¶åŸ¡ã hls.js é察å¿ãã©ãŠã¶åãã«å®è£
ã§ãããïŒãã€ã¡ãŒãžã§ããªããšææ»ããçºçãããã§ã ãã®ä»ãããã³ããšã³ãã§ã¯ <video> ã¿ã°ã®ãã©ãŠã¶æ¯ã®æåãã衚瀺ã®éãã«æéãããããŸãããïŒããçšåºŠäºæ³ã¯ããŠããŸãããããã¯ãã¡ãã£ã¢ã®åãæ±ãã¯é£ããâŠïŒ hls.js èªäœã¯å°å
¥ãæè»œã§ããµã¯ããš HLS åç»ã®ãã«ããã©ãŠã¶å¯Ÿå¿ãå®çŸã§ãããšãŠã䜿ããããã£ãã§ãã@types ãååšããã®ã§ãTypeScript ç°å¢ã§ãé£ãªãå®è£
ã§ããŸããã SSR ã HLS + AES-128 ã®åçã«ã察å¿ããŠããã®ã§ãèå³ã®ããæ¹ã¯äžåºŠ å
¬åŒããã¥ã¡ã³ã ã確èªããŠã¿ãŠãã ããã ãããã« åŸæ¥ãåç»é
ä¿¡ãµãŒãã¹ãæ§ç¯ããå Žåãffmpeg ãèŒãããšã³ã³ãŒããµãŒãããã¹ããªãŒãã³ã°é
ä¿¡ãµãŒããå¥å»ºãŠããŠãè² è·ã«å¿ããŠã¹ã±ãŒã«ãããŠâŠã®ãããªèšèšãå¿
èŠã ã£ãããšæããŸãã ä»åãMediaConvert ãã¯ãããšãã AWS ãµãŒãã¹ãš hls.js ãå©çšããããšã§ãæè»œã«ãã¹ã±ãŒã©ãã«ãªåç»ãšã³ã³ãŒãïŒHTTP ã¹ããªãŒãã³ã°é
ä¿¡ç°å¢ãæ§ç¯ããããšãã§ããŸããã ãžã§ãã¡ãã¬ãŒã®åç»éžèã¯ãŸã ãªãªãŒã¹ããã°ããã§ãã®ã§ãä»åŸåé¿ãèŠãªããããããªãæ¹åãéããŠãããããšæããŸããæåŸãŸã§ãèªã¿ããã ãããããšãããããŸããã åéã®äžèЧ | æ ªåŒäŒç€Ÿã¡ãã¬ãŒ ã¡ãã¬ãŒã®æ¡çšæ
å ±ã¯ãã¡ãããã確èªãã ããã www.medley.jp
ããã«ã¡ã¯ã第äžéçºã°ã«ãŒãã®ç¢éã§ãããžã§ãã¡ãã¬ãŒéçºãšã³ãžãã¢ãšããŠãäž»ã«ããã¯ãšã³ããæ
åœããŠããŸãã çŽè¿ã§ã¯ããžã§ãã¡ãã¬ãŒãå
æãªãªãŒã¹ãã ãåç»éžèã æ©èœã®éçºãããžã§ã¯ãã«æºãã£ãŠãããåç»ãã¡ã€ã«ã®ã¢ããããŒãïŒé
ä¿¡ç°å¢ã®èšèšã»å®è£
ãè¡ã£ãŠããŸããã ä»åã®ããã°ã§ã¯ããã®ãåç»éžèãæ©èœã®éçºã«å©çšãã AWS Elemental MediaConvert ãµãŒãã¹ãšã hls.js ãšãã OSS ã©ã€ãã©ãªã«ã€ããŠç޹ä»ããããšæããŸãã ãžã§ãã¡ãã¬ãŒã®ãåç»éžèãæ©èœ ã¯ããã«ãä»åãªãªãŒã¹ãããåç»éžèãæ©èœã«ã€ããŠæŠèŠã玹ä»ããŸãã æ°åã³ãããŠã€ã«ã¹æææ¡å€§ã«ãã£ãŠã察é¢ã§ã®é¢æ¥ã«äžå®ãæããããå
Œ
±äº€éæ©é¢ã®å©çšãé£ãããªã£ããããããšã«ãããæºè¶³ãªè»¢è·æŽ»åãã§ããªããªã£ãŠããæ¹ãããã£ãããããšæããŸãã ãã®ãããªèª²é¡ã解決ããããã«ããžã§ãã¡ãã¬ãŒã§ã¯ãªã¢ã«ã¿ã€ã ã«ãªã³ã©ã€ã³ã§é¢æ¥ãè¡ããWEB 颿¥ããšãäºæ¥è
ããããããèšå®ãã質åã«å¯ŸããŠå¿åè
ãåç»ã§åçãéããåç»éžèãã® 2 ã€ã®æ©èœãæäŸéå§ããããŸããã ref. WEB 颿¥ã»åç»éžèæ©èœã®ãªãªãŒã¹ã®ãç¥ãã åç»éžèïŒåç»é¢æ¥ïŒã¯ãè¿å¹Žå¢å åŸåã«ãããªã³ã©ã€ã³éžèã®äžçš®ã§ããäžè¬çã«ãæ±è·è
/ 就掻çã PC ã»ã¹ããŒããã©ã³çã®ã«ã¡ã©ã§ãäºãçšæãããèšåã«å¿ããŠåç»ãæ®åœ±ããäŒæ¥ã«éãããšã§éžèãè¡ããŸãã ref. WEB 颿¥ã»åç»éžèãšã¯ïŒ 宿œã®æµãã䜿çšããŒã«ããããŒã泚æç¹ãªã©ã培åºè§£èª¬ïŒ ç§ãã¡ãžã§ãã¡ãã¬ãŒã®åç»éžèã§ã¯ãäºæ¥æããããããèšå®ãã質åã«å¯ŸããŠãæ±è·è
ãåçåç»ãæåºããããšãã§ããŸããäºæ¥æãæ±è·è
ããåç»ã§è³ªåã»åçãéãããšã§ãæžé¡ã ãã§ã¯äŒãããªãé°å²æ°ã匷ã¿ãçžæã«äŒããããšãã§ããŸãã WEB 颿¥ã»åç»éžèæ©èœã®ãªãªãŒã¹ã®ãç¥ãã åç»é
ä¿¡ãµãŒãã¹ã®èšèšãã€ã³ã Web ã¢ããªã§ãã®ãããªåç»é
ä¿¡ãµãŒãã¹ãéçºããå ŽåãããŠãŒã¶ã«ããåç»ã¢ããããŒãç°å¢ããšããŠãŒã¶ãžã®åç»ã®é
ä¿¡ã»åçç°å¢ããæäŸããå¿
èŠããããŸãã ãžã§ãã¡ãã¬ãŒã§æ±ãåç»ã¯äžè¬å
¬éããããã®ã§ã¯ãªããå
¬éæ¡ä»¶ãè€éã§ãã ãã£ãŠä»åã¯ããã®ãåç»ã¢ããããŒãïŒé
ä¿¡ç°å¢ããèªãµãŒãã¹å
ã«æ§ç¯ããæ¹éããšãã以äžã®ãããªåç»ãŸããã®èšèšãã€ã³ãã«ã€ããŠæ€èšã»æè¡éžå®ãè¡ãããšã«ããŸããã ïŒãã¡ãããèŠä»¶ã«ãã£ãŠã¯ YouTube ããæ³äººåãåç»é
ä¿¡ãã©ãããã©ãŒã ãå¥çŽããæ¹ãæè»œãªå ŽåãããããšæããŸãïŒ åç»ã®é²ç»ã»æ®åœ± ãµããŒããããåç»ãã¡ã€ã«ã®ãã©ãŒããããã©ãããã Web ã¢ããªå
ã«é²ç»æ©èœãèšããã åç»ã®ã¢ããããŒãïŒã¹ãã¬ãŒãžïŒ åç»ãã¡ã€ã«ã®ããªããŒã·ã§ã³ã§ãåç»ãã¡ã€ã«ã®è§£æããè¡ãã åç»ãã¡ã€ã«ã®ã¢ããããŒãå
ïŒã¹ãã¬ãŒãžïŒãã©ãã«ããã åç»ã®ãšã³ã³ãŒã åç»ãã¡ã€ã«ã®ãšã³ã³ãŒã圢åŒïŒH.264ãHLS çïŒãã©ãããã éåæãšã³ã³ãŒãã®å Žåãã¹ããŒã¿ã¹æ€ç¥ã»ãšã©ãŒãã³ããªã³ã°ãã©ãããã åç»ã®é
ä¿¡ïŒããŠã³ããŒãïŒ é
信圢åŒïŒããŠã³ããŒãïŒã¹ããªãŒãã³ã°ïŒãã©ãããã æå·åãããå Žåã埩å·ãã©ã®ããã«è¡ãã åç»ãã¡ã€ã«ã®å
¬éæ¹æ³ïŒã¢ã¯ã»ã¹å¶éïŒãã©ãããã åç»ã®åç Web ããŒãžäžã§åçãããã®ãããã®å Žåã®è¡šç€ºã»åçå¶åŸ¡ãã©ãããã ãã©ãŠã¶ãµããŒããã©ããŸã§ã«ããããé察å¿ã»ãšã©ãŒæã®å¶åŸ¡ãã©ãããã ä»åã¯ãäžèšã®å€ªåã§èšèŒãã ãåç»ã®ãšã³ã³ãŒããã« MediaConvert ãã ãåç»ã®åçãã« hls.js ãããããæ¡çšããŠããŸãã åé
ã®è©³çްã¯çããŸãããå
šäœãéããŠå€§ãŸãã«ã以äžã®ãããŒã§ãåç»ã¢ããããŒã â ãšã³ã³ãŒãïŒå€æïŒâ é
ä¿¡ã»åçããå®çŸããããšã«ããŸããã ãã©ãŠã¶ãã Ajax ã§åç»ã S3 ãžã¢ããããŒããã MediaConvert ãåç»ã HLS 圢åŒã«ãšã³ã³ãŒãïŒå€æïŒãã ãã©ãŠã¶ã§ hls.js ã䜿ãåç»ã CloudFront ããã¹ããªãŒãã³ã°åœ¢åŒã§åä¿¡ãåçãã ä»åã¯ãã®ãåç»ã¢ããããŒã â ãšã³ã³ãŒãïŒå€æïŒâ é
ä¿¡ã»åçãã«çŠç¹ãçµããMediaConvert ãš hls.js ãã©ã®ããã«äœ¿ã£ãã®ãã玹ä»ããŸãã MediaConvert ã«ãã HLS ãšã³ã³ãŒã AWS Elemental MediaConvert ã¯ãS3 ãšã®èŠªåæ§ãé«ããã¡ã€ã«ããŒã¹ã®åç»å€æãµãŒãã¹ã§ããèªåã§ ffmpeg ãªã©ã䜿ã£ãŠåç»ãšã³ã³ãŒããµãŒããæ§ç¯ã»ç®¡çããããšãªããã¹ã±ãŒã©ãã«ãªåç»å€æåŠçãæè»œã«ã·ã¹ãã ã«çµã¿èŸŒãããšãã§ããŸãã ref. AWS Elemental MediaConvert æéã¯åºåããåç»ã®åçæéã«å¿ããåŸé課éã§ããAWS ã³ã³ãœãŒã«ãã GUI ããŒã¹ã§ãšã³ã³ãŒãèšå®ãäœæãããããžã§ãïŒãšã³ã³ãŒãåŠçïŒãç»é²ããããšãã§ããŸãã ãŸããä» AWS ãµãŒãã¹åæ§ã« API ãæäŸãããŠãããAWS CLI ãåèšèªã® SDK ã䜿ã£ãŠããã°ã©ã ãããšã³ã³ãŒãåŠçãç»é²ããããšãã§ããã·ã¹ãã 飿ºã容æã§ãã # CLI ã§ãšã³ã³ãŒããžã§ããç»é²ããäŸ $ aws --endpoint-url https://abcd1234.mediaconvert.region-name-1.amazonaws.com --region region-name-1 mediaconvert create-job --cli-input-json file://~/job.json äžèš CLI ã³ãã³ãã§äžã®ãããªãšã³ã³ãŒãèšå®ãèšèŒãã JSON ã䜿ããžã§ããäœæãããšãS3 äžã®åç»ãã¡ã€ã«ããµã¯ããšãšã³ã³ãŒãããŠãããŸãããžã§ãã¯ãã¥ãŒã€ã³ã°ãããå
éšã§äžŠååŠçãããããã倧éã®ãšã³ã³ãŒãèŠæ±ã«ãç°¡åã«å¿ããããšãã§ããŸãã { ... "Settings" : { "Inputs" : [ { # å
¥åå
ã® S3 ãã±ããäžã®åç»ãã¡ã€ã« key ãæå® "FileInput" : "s3://testcontent/720/example_input_720p.mov" } ], "OutputGroups" : [ { "OutputGroupSettings" : { "FileGroupSettings" : { # åºåå
ã® S3 ãã±ãã key ãæå® "Destination" : "s3://testbucket/output" } }, # åç»ã»é³å£°ã®ãšã³ã³ãŒãèšå®ãæå® # ããã§å質ã¬ãã«æ¯ã«æ¯ãåããè€æ°ã®ãã¡ã€ã«ãåºåããã # ãµã ãã€ã« jpg ãäœæãããããããšãå¯èœ "Outputs" : [ { "VideoDescription" : { ⊠}, "AudioDescriptions" : { ⊠} } ] } ] } } ref. AWSCLI ã䜿çšãã AWSElemental MediaConvertCreateJob ã®äŸ ãšã³ã³ãŒããå®äºãããžã§ãã¯ãcron + SDK ãªã©ã§ API ãä»ããŠå®æãã§ãã¯ããä»ã«ãCloudWatch Events ã«ããã€ãã³ãç£èŠ â Lambda ã§åŠçãããããªããšãã§ããŸãã ref. AWS Elemental MediaConvert ã«ãã CloudWatch ã€ãã³ã ã®äœ¿çš ãªãåç»ãåãšã³ã³ãŒãããã®ã éåžžããŠãŒã¶ããã¢ããããŒããããåç»ãã¡ã€ã«ã¯ãæ¢ã«äœããã®ã³ãŒããã¯ã§å§çž®ãã .mp4 ã .mov ãªã©ã®ã³ã³ãããã©ãŒãããã«å€æãããŠããããšãæ®ã©ã§ãã ããã Web ããŒãžã§ <video> ã¿ã°ã䜿ããããåç»ãã¡ã€ã«ãåçããããšããå Žåã ãåç»ãã©ãŒãããã«ãã©ãŠã¶ãé察å¿ã ãšåçã§ããªãã ãšããç°å¢äŸååé¡ããããŸãã ãã©ãŠã¶ãšåç»ãã©ãŒãããã®ãµããŒã衚 ref. HTML5 video > Browser support ãã®åé¡ã«å¯Ÿå¿ãããããå€ãã®åç»é
ä¿¡ãµãŒãã¹ã§ã¯ããŠãŒã¶ã®åç»ãå€ãã®ç°å¢ã§åçå¯èœãª MP4 ã³ã³ãããã©ãŒãããïŒH.264 + AAC ã³ãŒããã¯ïŒãªã©ã®åœ¢åŒãžãåãšã³ã³ãŒããããŠããŸãã ãžã§ãã¡ãã¬ãŒã®åç»éžèã§ã¯äžèšç®çã«å ããŠãåç»é²èЧæã®åç·ã»ç«¯æ«è² è·ãæãã ãHTTP ã¹ããªãŒãã³ã°åœ¢åŒã ã§åç»ãé
ä¿¡ããããã«ãã¢ããããŒããããåç»ãå
šãŠ HLS åœ¢åŒ ã«ãšã³ã³ãŒãããŠããŸãã HLS - HTTP Live Streaming åœ¢åŒ HLS 㯠HTTP Live Streaming ã®ç¥ã§ãApple 瀟ã®éçºããèŠæ Œã§ããHTTP ããŒã¹ã®ã¹ããªãŒãã³ã°éä¿¡ãããã³ã«ã§ã现åãã«ãã MP4 åç»ãã¡ã€ã«ãåå²ããŠã³ããŒããããããšã§åç»ã®ã¹ããªãŒãã³ã°é
ä¿¡ãå®çŸããŠããŸãã HLS 圢åŒã«ãšã³ã³ãŒããããåç»ã¯ .ts ãšããåå²ãããã¡ãã£ã¢ãã¡ã€ã«çŸ€ãšã .m3u8 ãšãããã¡ãã£ã¢ãã¡ã€ã«ã®ååŸå
ãç§æ°ãªã©ãèšèŒããããã¹ããã¡ã€ã«ã§æ§æãããŸãã .m3u8 ãã¡ã€ã«ã®äŸïŒãããã§ã¹ããã¡ã€ã«ããã¬ã€ãªã¹ããã¡ã€ã«ãšãïŒ #EXTM3U #EXT-X-TARGETDURATION:10 #EXT-X-VERSION:3 #EXT-X-MEDIA-SEQUENCE:0 #EXT-X-PLAYLIST-TYPE:VOD #EXTINF:9.97663, media-0.ts #EXTINF:9.97663, media-1.ts #EXTINF:7.10710, media-2.ts #EXT-X-ENDLIST ref. RFC 8216: HTTP Live Streaming HLS ã¯ä»ã®ã¹ããªãŒãã³ã°åœ¢åŒãšæ¯èŒããŠãã©ã€ãé
ä¿¡ / VOD ã©ã¡ãã«ã察å¿å¯èœãªããšã察å¿ãã©ãŠã¶ãå€ãããšãå°çšã®é
ä¿¡ãµãŒãã䜿ããã«é
ä¿¡å¯èœãªããšãªã©ãããè¿å¹Žã®åç»é
ä¿¡ãµãŒãã¹ã§åºãå©çšãããŠããŸãã Web ãšã³ãžãã¢ã®èŠç¹ããèŠãŠãã HTTP ããŒã¹ãªãããã£ãã·ã¥ã HTTPS æå·åãªã©ãæ¢å Web æè¡ãšæãåãããããšãæ³åãããããæ±ããããå°è±¡ã§ããã MediaConvert ã® HLS ãšã³ã³ãŒããžã§ãèšå® å®éã«ããã°ã©ã ãã API çµç±ã§ HLS ãšã³ã³ãŒããžã§ããç»é²ããéã®èšå® JSON ã¯ã以äžã®ããã« GUI ã§ãžã§ããã³ãã¬ãŒããäœæããŠç¢ºèªããããšãã§ããŸãã ãã®ã JSON ã衚瀺ãã§ãåè¿°ãã CLI ã³ãã³ã mediaconvert create-job --cli-input-json ã«æž¡ãã JSON ã衚瀺ãããŸããå®è£
ã®éã«ã¯ãã¡ããåèã«ããªããã ãŠãŒã¶ãŒã¬ã€ã ãåç
§ããŠå©çšãããæ©èœã«ããããèšå®ã远å ããŠããããšãããããããŸãã æ³šæç¹ã»ã€ãŸã¥ãããã€ã³ã å©çšåã« IAM ã§ MediaConvert çšããŒã«ã®èšå®ãå¿
èŠã§ã ã¹ããã 3. IAM æš©éã®èšå® AWS ã³ã³ãœãŒã«ã® Service Quotas > AWS ãµãŒãã¹ > AWS Elemental MediaConvert ãã確èªã§ããŸããããšã³ã³ãŒã䞊è¡åŠçã®åæå®è¡æ°äžé㯠20 ã«ãªã£ãŠããŸã AWS ã«ãŒãã¢ã«ãŠã³ã 1 ã€ã«ã€ã 1 ãµãŒãã¹ãå²åœãŠãããã®ã§ããããå¢ããããå Žåã¯ç³è«ãå¿
èŠã§ã ãšã³ã³ãŒããžã§ãããã¥ãŒã€ã³ã°ããããã¥ãŒããäœæããŠããžã§ãã®ç»é²æã«éžã¹ãã®ã§ãããäžèšããã䞊è¡åŠçã®åæå®è¡æ°äžéãã¯ãã®ããã¥ãŒãæ¯ã«åçã«æ¯ãåããããŸã äŸãã°ãæ¬çªãã¥ãŒããšãæ€èšŒãã¥ãŒãã® 2 ã€ã®ãã¥ãŒãäœæããå Žåãããããã®äžŠè¡åŠçã®åæå®è¡æ°äžé㯠10 ãã€ã«ãªãã®ã§æ³šæããŠãã ãã ãããã§ã¹ãæé圢åŒïŒManifest duration formatïŒã«æŽæ°ïŒINTEGERïŒãæå®ããŠãããšãiOS Safari ã§ãåç»ã®å®éã®åçæéãšãåçãã¬ã€ã€ãŒã®ã·ãŒã¯ããŒã«è¡šç€ºãããåèšæéã«ãºã¬ãçãããåé¡ããããŸãã æµ®åå°æ°ç¹ïŒFLOATING POINTïŒã«å€æŽããããšã§å¯Ÿå¿ããŸããããããã§ã¹ããã¡ã€ã«ã«åºåãããå .ts ãã¡ã€ã«ã®é·ãããæµ®åå°æ°ç¹ â æŽæ°ã«å€æããåãäžããããããšã§ãºã¬ãçããŠããããã§ãã hls.js ã«ãã HLS åç»ã®åçå¶åŸ¡ MediaConvert ã«ãã HLS 圢åŒã«ãšã³ã³ãŒããããåç»ããWeb ãã©ãŠã¶ã§åçããããã«å¿
èŠãªã®ããhls.js ã§ãã ref. video-dev/hls.js å®ã¯ HLS ã«ããã¹ããªãŒãã³ã°é
ä¿¡ã¯ãçŸç¶ Safari ãªã©éããããã©ãŠã¶ã§ãããã€ãã£ãã§ãµããŒããããŠããŸããã ref. https://caniuse.com/http-live-streaming ãã® HLS åç»ã Safari 以å€ã® Google Chrome ã IE11 ãªã©ã®äž»èŠãã©ãŠã¶ã§åçå¯èœã«ãããããhls.js ãå©çšãããŠããŸããå
éšçã«ã¯ãé察å¿ãã©ãŠã¶ç°å¢ã«ãããŠããã©ãŠã¶ã® MediaSource æ¡åŒµ ã䜿ã£ãŠ HLS åç»ãåçãã仿§ã«ãªã£ãŠããŸãã Video.js ãšã®æ¯èŒ 䌌ããããªã©ã€ãã©ãªã« Video.js ãšãããã®ããããå°å
¥ãè¿·ã£ãã®ã§ãã ⊠Video.js 㯠UI ãã»ããã«ãªã£ãã HLS ã«å¯Ÿå¿ããåçãã¬ã€ã€ãŒãã©ã€ãã©ãª HLS 察å¿ä»¥å€ã«ããåå¹ãç« åããªã©æ©èœãè±å¯ hls.js ã¯ãã©ãŠã¶æšæºã® <video> ã¿ã°ã§ HLS ã«å¯Ÿå¿ããããšã ããç®çã«ããã HLS ã¯ã©ã€ã¢ã³ããã©ã€ãã©ãª UI ãªã©ã¯ãªããåç»åçãã¬ã€ã€ãŒã¯ãã©ãŠã¶æšæºã®ãŸãŸ âŠãšãäžèšã®ããã« hls.js ã®æ¹ãã·ã³ãã«ã«ããããããšãå®çŸã§ãããããä»å㯠hls.js ãæ¡çšããŸããã GitHub ã®ã¹ã¿ãŒæ°ã¯å
çºã® Video.js ã®æ¹ãå€ãã®ã§ãããhls.js ãéçºã¯æŽ»çºã§ãæ¥æ¬ã§ã¯ ã°ãã·ãŒ ãããäžççã«ã¯ TED ã Twitter ã§ãæ¡çšãããŠãããååå®çžŸãããããšæããŸãã hls.js ã«ããå®è£
åºæ¬çã«ã¯ README ã® Getting Started ã®éãã§å®è£
ã§ããŸããäžéš README ã®ãµã³ãã«ã³ãŒãããæç²ããŠè§£èª¬ãããšâŠ var video = document . getElementById ( "video" ); var videoSrc = "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8" ; if ( Hls . isSupported ()) { var hls = new Hls (); hls . loadSource ( videoSrc ); hls . attachMedia ( video ); hls . on ( Hls . Events . MANIFEST_PARSED , function () { video . play (); }); } äžèš Hls.isSupported() ã®åå²ã§ãHLS ããã€ãã£ããµããŒãããŠããªããã©ãŠã¶ã®åŠçãå®è£
ããŠããŸãã æ¬æ¥ <video> ã® src 屿§ã«ã»ããããã¹ã .m3u8 ãã¡ã€ã«ã® URL ãž hls.loadSource() ã§ã¢ã¯ã»ã¹ãããã¯ã©ã€ã¢ã³ããã XHR ãªã¯ãšã¹ããé£ã°ããŸãããã®åŸ hls.attachMedia() ã§ã€ã³ã¹ã¿ã³ã¹ã DOM äžã® <video> ã¿ã°ã«çŽã¥ããŠããŸãã else if ( video . canPlayType ( 'application/vnd.apple.mpegurl' )) { video . src = videoSrc ; video . addEventListener ( 'loadedmetadata' , function () { video . play (); }); } äžèšã®åå²ã iOS Safari ãªã©ãHLS åç»ããã€ãã£ããµããŒãããŠãããã©ãŠã¶åãã®åŠçã§ããåçŽã« .m3u8 ãžã® URL ã <video> ã¿ã°ã® src ãžä»äžããŠããã ãã§ããã ïŒãµã³ãã«ã³ãŒãã§ã¯ããããã§ã¹ããã¡ã€ã«ã®ããŒãåŸã«èªååçãããããã«ãªã£ãŠããããã§ãïŒ æ³šæç¹ã»ã€ãŸã¥ãããã€ã³ã hls.js ã¯ã©ã€ã¢ã³ããååŸãã HLS åç»ãã¡ã€ã«çŸ€ã¯ãCORS ãããã§ GET ãªã¯ãšã¹ããèš±å¯ãããç°å¢ã«èšçœ®ããå¿
èŠããããŸã .m3u8 ãããã§ã¹ããã¡ã€ã«ãã¢ããªã® API ãªã©ããè¿åŽããå ŽåãContent-Type ã application/x-mpegURL ã«ããŠæž¡ãå¿
èŠããããŸã iOS Safari ãªã©ã® hls.js é察å¿ãã©ãŠã¶åãã®å®è£
ãæèããå¿
èŠããããŸã hls.js ã«ããå¶åŸ¡ãè€éã«ãªãã±ãŒã¹ã§ã¯ãåããããªå¶åŸ¡ã hls.js é察å¿ãã©ãŠã¶åãã«å®è£
ã§ãããïŒãã€ã¡ãŒãžã§ããªããšææ»ããçºçãããã§ã ãã®ä»ãããã³ããšã³ãã§ã¯ <video> ã¿ã°ã®ãã©ãŠã¶æ¯ã®æåãã衚瀺ã®éãã«æéãããããŸãããïŒããçšåºŠäºæ³ã¯ããŠããŸãããããã¯ãã¡ãã£ã¢ã®åãæ±ãã¯é£ããâŠïŒ hls.js èªäœã¯å°å
¥ãæè»œã§ããµã¯ããš HLS åç»ã®ãã«ããã©ãŠã¶å¯Ÿå¿ãå®çŸã§ãããšãŠã䜿ããããã£ãã§ãã@types ãååšããã®ã§ãTypeScript ç°å¢ã§ãé£ãªãå®è£
ã§ããŸããã SSR ã HLS + AES-128 ã®åçã«ã察å¿ããŠããã®ã§ãèå³ã®ããæ¹ã¯äžåºŠ å
¬åŒããã¥ã¡ã³ã ã確èªããŠã¿ãŠãã ããã ãããã« åŸæ¥ãåç»é
ä¿¡ãµãŒãã¹ãæ§ç¯ããå Žåãffmpeg ãèŒãããšã³ã³ãŒããµãŒãããã¹ããªãŒãã³ã°é
ä¿¡ãµãŒããå¥å»ºãŠããŠãè² è·ã«å¿ããŠã¹ã±ãŒã«ãããŠâŠã®ãããªèšèšãå¿
èŠã ã£ãããšæããŸãã ä»åãMediaConvert ãã¯ãããšãã AWS ãµãŒãã¹ãš hls.js ãå©çšããããšã§ãæè»œã«ãã¹ã±ãŒã©ãã«ãªåç»ãšã³ã³ãŒãïŒHTTP ã¹ããªãŒãã³ã°é
ä¿¡ç°å¢ãæ§ç¯ããããšãã§ããŸããã ãžã§ãã¡ãã¬ãŒã®åç»éžèã¯ãŸã ãªãªãŒã¹ããã°ããã§ãã®ã§ãä»åŸåé¿ãèŠãªããããããªãæ¹åãéããŠãããããšæããŸããæåŸãŸã§ãèªã¿ããã ãããããšãããããŸããã åéã®äžèЧ | æ ªåŒäŒç€Ÿã¡ãã¬ãŒ ã¡ãã¬ãŒã®æ¡çšæ
å ±ã¯ãã¡ãããã確èªãã ããã www.medley.jp
ããã«ã¡ã¯ã第äžéçºã°ã«ãŒãã®ç¢éã§ãããžã§ãã¡ãã¬ãŒéçºãšã³ãžãã¢ãšããŠãäž»ã«ããã¯ãšã³ããæ
åœããŠããŸãã çŽè¿ã§ã¯ããžã§ãã¡ãã¬ãŒãå
æãªãªãŒã¹ãã ãåç»éžèã æ©èœã®éçºãããžã§ã¯ãã«æºãã£ãŠãããåç»ãã¡ã€ã«ã®ã¢ããããŒãïŒé
ä¿¡ç°å¢ã®èšèšã»å®è£
ãè¡ã£ãŠããŸããã ä»åã®ããã°ã§ã¯ããã®ãåç»éžèãæ©èœã®éçºã«å©çšãã AWS Elemental MediaConvert ãµãŒãã¹ãšã hls.js ãšãã OSS ã©ã€ãã©ãªã«ã€ããŠç޹ä»ããããšæããŸãã ãžã§ãã¡ãã¬ãŒã®ãåç»éžèãæ©èœ ã¯ããã«ãä»åãªãªãŒã¹ãããåç»éžèãæ©èœã«ã€ããŠæŠèŠã玹ä»ããŸãã æ°åã³ãããŠã€ã«ã¹æææ¡å€§ã«ãã£ãŠã察é¢ã§ã®é¢æ¥ã«äžå®ãæããããå
Œ
±äº€éæ©é¢ã®å©çšãé£ãããªã£ããããããšã«ãããæºè¶³ãªè»¢è·æŽ»åãã§ããªããªã£ãŠããæ¹ãããã£ãããããšæããŸãã ãã®ãããªèª²é¡ã解決ããããã«ããžã§ãã¡ãã¬ãŒã§ã¯ãªã¢ã«ã¿ã€ã ã«ãªã³ã©ã€ã³ã§é¢æ¥ãè¡ããWEB 颿¥ããšãäºæ¥è
ããããããèšå®ãã質åã«å¯ŸããŠå¿åè
ãåç»ã§åçãéããåç»éžèãã® 2 ã€ã®æ©èœãæäŸéå§ããããŸããã ref. WEB 颿¥ã»åç»éžèæ©èœã®ãªãªãŒã¹ã®ãç¥ãã åç»éžèïŒåç»é¢æ¥ïŒã¯ãè¿å¹Žå¢å åŸåã«ãããªã³ã©ã€ã³éžèã®äžçš®ã§ããäžè¬çã«ãæ±è·è
/ 就掻çã PC ã»ã¹ããŒããã©ã³çã®ã«ã¡ã©ã§ãäºãçšæãããèšåã«å¿ããŠåç»ãæ®åœ±ããäŒæ¥ã«éãããšã§éžèãè¡ããŸãã ref. WEB 颿¥ã»åç»éžèãšã¯ïŒ 宿œã®æµãã䜿çšããŒã«ããããŒã泚æç¹ãªã©ã培åºè§£èª¬ïŒ ç§ãã¡ãžã§ãã¡ãã¬ãŒã®åç»éžèã§ã¯ãäºæ¥æããããããèšå®ãã質åã«å¯ŸããŠãæ±è·è
ãåçåç»ãæåºããããšãã§ããŸããäºæ¥æãæ±è·è
ããåç»ã§è³ªåã»åçãéãããšã§ãæžé¡ã ãã§ã¯äŒãããªãé°å²æ°ã匷ã¿ãçžæã«äŒããããšãã§ããŸãã WEB 颿¥ã»åç»éžèæ©èœã®ãªãªãŒã¹ã®ãç¥ãã åç»é
ä¿¡ãµãŒãã¹ã®èšèšãã€ã³ã Web ã¢ããªã§ãã®ãããªåç»é
ä¿¡ãµãŒãã¹ãéçºããå ŽåãããŠãŒã¶ã«ããåç»ã¢ããããŒãç°å¢ããšããŠãŒã¶ãžã®åç»ã®é
ä¿¡ã»åçç°å¢ããæäŸããå¿
èŠããããŸãã ãžã§ãã¡ãã¬ãŒã§æ±ãåç»ã¯äžè¬å
¬éããããã®ã§ã¯ãªããå
¬éæ¡ä»¶ãè€éã§ãã ãã£ãŠä»åã¯ããã®ãåç»ã¢ããããŒãïŒé
ä¿¡ç°å¢ããèªãµãŒãã¹å
ã«æ§ç¯ããæ¹éããšãã以äžã®ãããªåç»ãŸããã®èšèšãã€ã³ãã«ã€ããŠæ€èšã»æè¡éžå®ãè¡ãããšã«ããŸããã ïŒãã¡ãããèŠä»¶ã«ãã£ãŠã¯ YouTube ããæ³äººåãåç»é
ä¿¡ãã©ãããã©ãŒã ãå¥çŽããæ¹ãæè»œãªå ŽåãããããšæããŸãïŒ åç»ã®é²ç»ã»æ®åœ± ãµããŒããããåç»ãã¡ã€ã«ã®ãã©ãŒããããã©ãããã Web ã¢ããªå
ã«é²ç»æ©èœãèšããã åç»ã®ã¢ããããŒãïŒã¹ãã¬ãŒãžïŒ åç»ãã¡ã€ã«ã®ããªããŒã·ã§ã³ã§ãåç»ãã¡ã€ã«ã®è§£æããè¡ãã åç»ãã¡ã€ã«ã®ã¢ããããŒãå
ïŒã¹ãã¬ãŒãžïŒãã©ãã«ããã åç»ã®ãšã³ã³ãŒã åç»ãã¡ã€ã«ã®ãšã³ã³ãŒã圢åŒïŒH.264ãHLS çïŒãã©ãããã éåæãšã³ã³ãŒãã®å Žåãã¹ããŒã¿ã¹æ€ç¥ã»ãšã©ãŒãã³ããªã³ã°ãã©ãããã åç»ã®é
ä¿¡ïŒããŠã³ããŒãïŒ é
信圢åŒïŒããŠã³ããŒãïŒã¹ããªãŒãã³ã°ïŒãã©ãããã æå·åãããå Žåã埩å·ãã©ã®ããã«è¡ãã åç»ãã¡ã€ã«ã®å
¬éæ¹æ³ïŒã¢ã¯ã»ã¹å¶éïŒãã©ãããã åç»ã®åç Web ããŒãžäžã§åçãããã®ãããã®å Žåã®è¡šç€ºã»åçå¶åŸ¡ãã©ãããã ãã©ãŠã¶ãµããŒããã©ããŸã§ã«ããããé察å¿ã»ãšã©ãŒæã®å¶åŸ¡ãã©ãããã ä»åã¯ãäžèšã®å€ªåã§èšèŒãã ãåç»ã®ãšã³ã³ãŒããã« MediaConvert ãã ãåç»ã®åçãã« hls.js ãããããæ¡çšããŠããŸãã åé
ã®è©³çްã¯çããŸãããå
šäœãéããŠå€§ãŸãã«ã以äžã®ãããŒã§ãåç»ã¢ããããŒã â ãšã³ã³ãŒãïŒå€æïŒâ é
ä¿¡ã»åçããå®çŸããããšã«ããŸããã ãã©ãŠã¶ãã Ajax ã§åç»ã S3 ãžã¢ããããŒããã MediaConvert ãåç»ã HLS 圢åŒã«ãšã³ã³ãŒãïŒå€æïŒãã ãã©ãŠã¶ã§ hls.js ã䜿ãåç»ã CloudFront ããã¹ããªãŒãã³ã°åœ¢åŒã§åä¿¡ãåçãã ä»åã¯ãã®ãåç»ã¢ããããŒã â ãšã³ã³ãŒãïŒå€æïŒâ é
ä¿¡ã»åçãã«çŠç¹ãçµããMediaConvert ãš hls.js ãã©ã®ããã«äœ¿ã£ãã®ãã玹ä»ããŸãã MediaConvert ã«ãã HLS ãšã³ã³ãŒã AWS Elemental MediaConvert ã¯ãS3 ãšã®èŠªåæ§ãé«ããã¡ã€ã«ããŒã¹ã®åç»å€æãµãŒãã¹ã§ããèªåã§ ffmpeg ãªã©ã䜿ã£ãŠåç»ãšã³ã³ãŒããµãŒããæ§ç¯ã»ç®¡çããããšãªããã¹ã±ãŒã©ãã«ãªåç»å€æåŠçãæè»œã«ã·ã¹ãã ã«çµã¿èŸŒãããšãã§ããŸãã ref. AWS Elemental MediaConvert æéã¯åºåããåç»ã®åçæéã«å¿ããåŸé課éã§ããAWS ã³ã³ãœãŒã«ãã GUI ããŒã¹ã§ãšã³ã³ãŒãèšå®ãäœæãããããžã§ãïŒãšã³ã³ãŒãåŠçïŒãç»é²ããããšãã§ããŸãã ãŸããä» AWS ãµãŒãã¹åæ§ã« API ãæäŸãããŠãããAWS CLI ãåèšèªã® SDK ã䜿ã£ãŠããã°ã©ã ãããšã³ã³ãŒãåŠçãç»é²ããããšãã§ããã·ã¹ãã 飿ºã容æã§ãã # CLI ã§ãšã³ã³ãŒããžã§ããç»é²ããäŸ $ aws --endpoint-url https://abcd1234.mediaconvert.region-name-1.amazonaws.com --region region-name-1 mediaconvert create-job --cli-input-json file://~/job.json äžèš CLI ã³ãã³ãã§äžã®ãããªãšã³ã³ãŒãèšå®ãèšèŒãã JSON ã䜿ããžã§ããäœæãããšãS3 äžã®åç»ãã¡ã€ã«ããµã¯ããšãšã³ã³ãŒãããŠãããŸãããžã§ãã¯ãã¥ãŒã€ã³ã°ãããå
éšã§äžŠååŠçãããããã倧éã®ãšã³ã³ãŒãèŠæ±ã«ãç°¡åã«å¿ããããšãã§ããŸãã { ... "Settings" : { "Inputs" : [ { # å
¥åå
ã® S3 ãã±ããäžã®åç»ãã¡ã€ã« key ãæå® "FileInput" : "s3://testcontent/720/example_input_720p.mov" } ], "OutputGroups" : [ { "OutputGroupSettings" : { "FileGroupSettings" : { # åºåå
ã® S3 ãã±ãã key ãæå® "Destination" : "s3://testbucket/output" } }, # åç»ã»é³å£°ã®ãšã³ã³ãŒãèšå®ãæå® # ããã§å質ã¬ãã«æ¯ã«æ¯ãåããè€æ°ã®ãã¡ã€ã«ãåºåããã # ãµã ãã€ã« jpg ãäœæãããããããšãå¯èœ "Outputs" : [ { "VideoDescription" : { ⊠}, "AudioDescriptions" : { ⊠} } ] } ] } } ref. AWSCLI ã䜿çšãã AWSElemental MediaConvertCreateJob ã®äŸ ãšã³ã³ãŒããå®äºãããžã§ãã¯ãcron + SDK ãªã©ã§ API ãä»ããŠå®æãã§ãã¯ããä»ã«ãCloudWatch Events ã«ããã€ãã³ãç£èŠ â Lambda ã§åŠçãããããªããšãã§ããŸãã ref. AWS Elemental MediaConvert ã«ãã CloudWatch ã€ãã³ã ã®äœ¿çš ãªãåç»ãåãšã³ã³ãŒãããã®ã éåžžããŠãŒã¶ããã¢ããããŒããããåç»ãã¡ã€ã«ã¯ãæ¢ã«äœããã®ã³ãŒããã¯ã§å§çž®ãã .mp4 ã .mov ãªã©ã®ã³ã³ãããã©ãŒãããã«å€æãããŠããããšãæ®ã©ã§ãã ããã Web ããŒãžã§ <video> ã¿ã°ã䜿ããããåç»ãã¡ã€ã«ãåçããããšããå Žåã ãåç»ãã©ãŒãããã«ãã©ãŠã¶ãé察å¿ã ãšåçã§ããªãã ãšããç°å¢äŸååé¡ããããŸãã ãã©ãŠã¶ãšåç»ãã©ãŒãããã®ãµããŒã衚 ref. HTML5 video > Browser support ãã®åé¡ã«å¯Ÿå¿ãããããå€ãã®åç»é
ä¿¡ãµãŒãã¹ã§ã¯ããŠãŒã¶ã®åç»ãå€ãã®ç°å¢ã§åçå¯èœãª MP4 ã³ã³ãããã©ãŒãããïŒH.264 + AAC ã³ãŒããã¯ïŒãªã©ã®åœ¢åŒãžãåãšã³ã³ãŒããããŠããŸãã ãžã§ãã¡ãã¬ãŒã®åç»éžèã§ã¯äžèšç®çã«å ããŠãåç»é²èЧæã®åç·ã»ç«¯æ«è² è·ãæãã ãHTTP ã¹ããªãŒãã³ã°åœ¢åŒã ã§åç»ãé
ä¿¡ããããã«ãã¢ããããŒããããåç»ãå
šãŠ HLS åœ¢åŒ ã«ãšã³ã³ãŒãããŠããŸãã HLS - HTTP Live Streaming åœ¢åŒ HLS 㯠HTTP Live Streaming ã®ç¥ã§ãApple 瀟ã®éçºããèŠæ Œã§ããHTTP ããŒã¹ã®ã¹ããªãŒãã³ã°éä¿¡ãããã³ã«ã§ã现åãã«ãã MP4 åç»ãã¡ã€ã«ãåå²ããŠã³ããŒããããããšã§åç»ã®ã¹ããªãŒãã³ã°é
ä¿¡ãå®çŸããŠããŸãã HLS 圢åŒã«ãšã³ã³ãŒããããåç»ã¯ .ts ãšããåå²ãããã¡ãã£ã¢ãã¡ã€ã«çŸ€ãšã .m3u8 ãšãããã¡ãã£ã¢ãã¡ã€ã«ã®ååŸå
ãç§æ°ãªã©ãèšèŒããããã¹ããã¡ã€ã«ã§æ§æãããŸãã .m3u8 ãã¡ã€ã«ã®äŸïŒãããã§ã¹ããã¡ã€ã«ããã¬ã€ãªã¹ããã¡ã€ã«ãšãïŒ #EXTM3U #EXT-X-TARGETDURATION:10 #EXT-X-VERSION:3 #EXT-X-MEDIA-SEQUENCE:0 #EXT-X-PLAYLIST-TYPE:VOD #EXTINF:9.97663, media-0.ts #EXTINF:9.97663, media-1.ts #EXTINF:7.10710, media-2.ts #EXT-X-ENDLIST ref. RFC 8216: HTTP Live Streaming HLS ã¯ä»ã®ã¹ããªãŒãã³ã°åœ¢åŒãšæ¯èŒããŠãã©ã€ãé
ä¿¡ / VOD ã©ã¡ãã«ã察å¿å¯èœãªããšã察å¿ãã©ãŠã¶ãå€ãããšãå°çšã®é
ä¿¡ãµãŒãã䜿ããã«é
ä¿¡å¯èœãªããšãªã©ãããè¿å¹Žã®åç»é
ä¿¡ãµãŒãã¹ã§åºãå©çšãããŠããŸãã Web ãšã³ãžãã¢ã®èŠç¹ããèŠãŠãã HTTP ããŒã¹ãªãããã£ãã·ã¥ã HTTPS æå·åãªã©ãæ¢å Web æè¡ãšæãåãããããšãæ³åãããããæ±ããããå°è±¡ã§ããã MediaConvert ã® HLS ãšã³ã³ãŒããžã§ãèšå® å®éã«ããã°ã©ã ãã API çµç±ã§ HLS ãšã³ã³ãŒããžã§ããç»é²ããéã®èšå® JSON ã¯ã以äžã®ããã« GUI ã§ãžã§ããã³ãã¬ãŒããäœæããŠç¢ºèªããããšãã§ããŸãã ãã®ã JSON ã衚瀺ãã§ãåè¿°ãã CLI ã³ãã³ã mediaconvert create-job --cli-input-json ã«æž¡ãã JSON ã衚瀺ãããŸããå®è£
ã®éã«ã¯ãã¡ããåèã«ããªããã ãŠãŒã¶ãŒã¬ã€ã ãåç
§ããŠå©çšãããæ©èœã«ããããèšå®ã远å ããŠããããšãããããããŸãã æ³šæç¹ã»ã€ãŸã¥ãããã€ã³ã å©çšåã« IAM ã§ MediaConvert çšããŒã«ã®èšå®ãå¿
èŠã§ã ã¹ããã 3. IAM æš©éã®èšå® AWS ã³ã³ãœãŒã«ã® Service Quotas > AWS ãµãŒãã¹ > AWS Elemental MediaConvert ãã確èªã§ããŸããããšã³ã³ãŒã䞊è¡åŠçã®åæå®è¡æ°äžé㯠20 ã«ãªã£ãŠããŸã AWS ã«ãŒãã¢ã«ãŠã³ã 1 ã€ã«ã€ã 1 ãµãŒãã¹ãå²åœãŠãããã®ã§ããããå¢ããããå Žåã¯ç³è«ãå¿
èŠã§ã ãšã³ã³ãŒããžã§ãããã¥ãŒã€ã³ã°ããããã¥ãŒããäœæããŠããžã§ãã®ç»é²æã«éžã¹ãã®ã§ãããäžèšããã䞊è¡åŠçã®åæå®è¡æ°äžéãã¯ãã®ããã¥ãŒãæ¯ã«åçã«æ¯ãåããããŸã äŸãã°ãæ¬çªãã¥ãŒããšãæ€èšŒãã¥ãŒãã® 2 ã€ã®ãã¥ãŒãäœæããå Žåãããããã®äžŠè¡åŠçã®åæå®è¡æ°äžé㯠10 ãã€ã«ãªãã®ã§æ³šæããŠãã ãã ãããã§ã¹ãæé圢åŒïŒManifest duration formatïŒã«æŽæ°ïŒINTEGERïŒãæå®ããŠãããšãiOS Safari ã§ãåç»ã®å®éã®åçæéãšãåçãã¬ã€ã€ãŒã®ã·ãŒã¯ããŒã«è¡šç€ºãããåèšæéã«ãºã¬ãçãããåé¡ããããŸãã æµ®åå°æ°ç¹ïŒFLOATING POINTïŒã«å€æŽããããšã§å¯Ÿå¿ããŸããããããã§ã¹ããã¡ã€ã«ã«åºåãããå .ts ãã¡ã€ã«ã®é·ãããæµ®åå°æ°ç¹ â æŽæ°ã«å€æããåãäžããããããšã§ãºã¬ãçããŠããããã§ãã hls.js ã«ãã HLS åç»ã®åçå¶åŸ¡ MediaConvert ã«ãã HLS 圢åŒã«ãšã³ã³ãŒããããåç»ããWeb ãã©ãŠã¶ã§åçããããã«å¿
èŠãªã®ããhls.js ã§ãã ref. video-dev/hls.js å®ã¯ HLS ã«ããã¹ããªãŒãã³ã°é
ä¿¡ã¯ãçŸç¶ Safari ãªã©éããããã©ãŠã¶ã§ãããã€ãã£ãã§ãµããŒããããŠããŸããã ref. https://caniuse.com/http-live-streaming ãã® HLS åç»ã Safari 以å€ã® Google Chrome ã IE11 ãªã©ã®äž»èŠãã©ãŠã¶ã§åçå¯èœã«ãããããhls.js ãå©çšãããŠããŸããå
éšçã«ã¯ãé察å¿ãã©ãŠã¶ç°å¢ã«ãããŠããã©ãŠã¶ã® MediaSource æ¡åŒµ ã䜿ã£ãŠ HLS åç»ãåçãã仿§ã«ãªã£ãŠããŸãã Video.js ãšã®æ¯èŒ 䌌ããããªã©ã€ãã©ãªã« Video.js ãšãããã®ããããå°å
¥ãè¿·ã£ãã®ã§ãã ⊠Video.js 㯠UI ãã»ããã«ãªã£ãã HLS ã«å¯Ÿå¿ããåçãã¬ã€ã€ãŒãã©ã€ãã©ãª HLS 察å¿ä»¥å€ã«ããåå¹ãç« åããªã©æ©èœãè±å¯ hls.js ã¯ãã©ãŠã¶æšæºã® <video> ã¿ã°ã§ HLS ã«å¯Ÿå¿ããããšã ããç®çã«ããã HLS ã¯ã©ã€ã¢ã³ããã©ã€ãã©ãª UI ãªã©ã¯ãªããåç»åçãã¬ã€ã€ãŒã¯ãã©ãŠã¶æšæºã®ãŸãŸ âŠãšãäžèšã®ããã« hls.js ã®æ¹ãã·ã³ãã«ã«ããããããšãå®çŸã§ãããããä»å㯠hls.js ãæ¡çšããŸããã GitHub ã®ã¹ã¿ãŒæ°ã¯å
çºã® Video.js ã®æ¹ãå€ãã®ã§ãããhls.js ãéçºã¯æŽ»çºã§ãæ¥æ¬ã§ã¯ ã°ãã·ãŒ ãããäžççã«ã¯ TED ã Twitter ã§ãæ¡çšãããŠãããååå®çžŸãããããšæããŸãã hls.js ã«ããå®è£
åºæ¬çã«ã¯ README ã® Getting Started ã®éãã§å®è£
ã§ããŸããäžéš README ã®ãµã³ãã«ã³ãŒãããæç²ããŠè§£èª¬ãããšâŠ var video = document . getElementById ( "video" ); var videoSrc = "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8" ; if ( Hls . isSupported ()) { var hls = new Hls (); hls . loadSource ( videoSrc ); hls . attachMedia ( video ); hls . on ( Hls . Events . MANIFEST_PARSED , function () { video . play (); }); } äžèš Hls.isSupported() ã®åå²ã§ãHLS ããã€ãã£ããµããŒãããŠããªããã©ãŠã¶ã®åŠçãå®è£
ããŠããŸãã æ¬æ¥ <video> ã® src 屿§ã«ã»ããããã¹ã .m3u8 ãã¡ã€ã«ã® URL ãž hls.loadSource() ã§ã¢ã¯ã»ã¹ãããã¯ã©ã€ã¢ã³ããã XHR ãªã¯ãšã¹ããé£ã°ããŸãããã®åŸ hls.attachMedia() ã§ã€ã³ã¹ã¿ã³ã¹ã DOM äžã® <video> ã¿ã°ã«çŽã¥ããŠããŸãã else if ( video . canPlayType ( 'application/vnd.apple.mpegurl' )) { video . src = videoSrc ; video . addEventListener ( 'loadedmetadata' , function () { video . play (); }); } äžèšã®åå²ã iOS Safari ãªã©ãHLS åç»ããã€ãã£ããµããŒãããŠãããã©ãŠã¶åãã®åŠçã§ããåçŽã« .m3u8 ãžã® URL ã <video> ã¿ã°ã® src ãžä»äžããŠããã ãã§ããã ïŒãµã³ãã«ã³ãŒãã§ã¯ããããã§ã¹ããã¡ã€ã«ã®ããŒãåŸã«èªååçãããããã«ãªã£ãŠããããã§ãïŒ æ³šæç¹ã»ã€ãŸã¥ãããã€ã³ã hls.js ã¯ã©ã€ã¢ã³ããååŸãã HLS åç»ãã¡ã€ã«çŸ€ã¯ãCORS ãããã§ GET ãªã¯ãšã¹ããèš±å¯ãããç°å¢ã«èšçœ®ããå¿
èŠããããŸã .m3u8 ãããã§ã¹ããã¡ã€ã«ãã¢ããªã® API ãªã©ããè¿åŽããå ŽåãContent-Type ã application/x-mpegURL ã«ããŠæž¡ãå¿
èŠããããŸã iOS Safari ãªã©ã® hls.js é察å¿ãã©ãŠã¶åãã®å®è£
ãæèããå¿
èŠããããŸã hls.js ã«ããå¶åŸ¡ãè€éã«ãªãã±ãŒã¹ã§ã¯ãåããããªå¶åŸ¡ã hls.js é察å¿ãã©ãŠã¶åãã«å®è£
ã§ãããïŒãã€ã¡ãŒãžã§ããªããšææ»ããçºçãããã§ã ãã®ä»ãããã³ããšã³ãã§ã¯ <video> ã¿ã°ã®ãã©ãŠã¶æ¯ã®æåãã衚瀺ã®éãã«æéãããããŸãããïŒããçšåºŠäºæ³ã¯ããŠããŸãããããã¯ãã¡ãã£ã¢ã®åãæ±ãã¯é£ããâŠïŒ hls.js èªäœã¯å°å
¥ãæè»œã§ããµã¯ããš HLS åç»ã®ãã«ããã©ãŠã¶å¯Ÿå¿ãå®çŸã§ãããšãŠã䜿ããããã£ãã§ãã@types ãååšããã®ã§ãTypeScript ç°å¢ã§ãé£ãªãå®è£
ã§ããŸããã SSR ã HLS + AES-128 ã®åçã«ã察å¿ããŠããã®ã§ãèå³ã®ããæ¹ã¯äžåºŠ å
¬åŒããã¥ã¡ã³ã ã確èªããŠã¿ãŠãã ããã ãããã« åŸæ¥ãåç»é
ä¿¡ãµãŒãã¹ãæ§ç¯ããå Žåãffmpeg ãèŒãããšã³ã³ãŒããµãŒãããã¹ããªãŒãã³ã°é
ä¿¡ãµãŒããå¥å»ºãŠããŠãè² è·ã«å¿ããŠã¹ã±ãŒã«ãããŠâŠã®ãããªèšèšãå¿
èŠã ã£ãããšæããŸãã ä»åãMediaConvert ãã¯ãããšãã AWS ãµãŒãã¹ãš hls.js ãå©çšããããšã§ãæè»œã«ãã¹ã±ãŒã©ãã«ãªåç»ãšã³ã³ãŒãïŒHTTP ã¹ããªãŒãã³ã°é
ä¿¡ç°å¢ãæ§ç¯ããããšãã§ããŸããã ãžã§ãã¡ãã¬ãŒã®åç»éžèã¯ãŸã ãªãªãŒã¹ããã°ããã§ãã®ã§ãä»åŸåé¿ãèŠãªããããããªãæ¹åãéããŠãããããšæããŸããæåŸãŸã§ãèªã¿ããã ãããããšãããããŸããã åéã®äžèЧ | æ ªåŒäŒç€Ÿã¡ãã¬ãŒ ã¡ãã¬ãŒã®æ¡çšæ
å ±ã¯ãã¡ãããã確èªãã ããã www.medley.jp
ããã«ã¡ã¯ã第äžéçºã°ã«ãŒãã®ç¢éã§ãããžã§ãã¡ãã¬ãŒéçºãšã³ãžãã¢ãšããŠãäž»ã«ããã¯ãšã³ããæ
åœããŠããŸãã çŽè¿ã§ã¯ããžã§ãã¡ãã¬ãŒãå
æãªãªãŒã¹ãã ãåç»éžèã æ©èœã®éçºãããžã§ã¯ãã«æºãã£ãŠãããåç»ãã¡ã€ã«ã®ã¢ããããŒãïŒé
ä¿¡ç°å¢ã®èšèšã»å®è£
ãè¡ã£ãŠããŸããã ä»åã®ããã°ã§ã¯ããã®ãåç»éžèãæ©èœã®éçºã«å©çšãã AWS Elemental MediaConvert ãµãŒãã¹ãšã hls.js ãšãã OSS ã©ã€ãã©ãªã«ã€ããŠç޹ä»ããããšæããŸãã ãžã§ãã¡ãã¬ãŒã®ãåç»éžèãæ©èœ ã¯ããã«ãä»åãªãªãŒã¹ãããåç»éžèãæ©èœã«ã€ããŠæŠèŠã玹ä»ããŸãã æ°åã³ãããŠã€ã«ã¹æææ¡å€§ã«ãã£ãŠã察é¢ã§ã®é¢æ¥ã«äžå®ãæããããå
Œ
±äº€éæ©é¢ã®å©çšãé£ãããªã£ããããããšã«ãããæºè¶³ãªè»¢è·æŽ»åãã§ããªããªã£ãŠããæ¹ãããã£ãããããšæããŸãã ãã®ãããªèª²é¡ã解決ããããã«ããžã§ãã¡ãã¬ãŒã§ã¯ãªã¢ã«ã¿ã€ã ã«ãªã³ã©ã€ã³ã§é¢æ¥ãè¡ããWEB 颿¥ããšãäºæ¥è
ããããããèšå®ãã質åã«å¯ŸããŠå¿åè
ãåç»ã§åçãéããåç»éžèãã® 2 ã€ã®æ©èœãæäŸéå§ããããŸããã ref. WEB 颿¥ã»åç»éžèæ©èœã®ãªãªãŒã¹ã®ãç¥ãã åç»éžèïŒåç»é¢æ¥ïŒã¯ãè¿å¹Žå¢å åŸåã«ãããªã³ã©ã€ã³éžèã®äžçš®ã§ããäžè¬çã«ãæ±è·è
/ 就掻çã PC ã»ã¹ããŒããã©ã³çã®ã«ã¡ã©ã§ãäºãçšæãããèšåã«å¿ããŠåç»ãæ®åœ±ããäŒæ¥ã«éãããšã§éžèãè¡ããŸãã ref. WEB 颿¥ã»åç»éžèãšã¯ïŒ 宿œã®æµãã䜿çšããŒã«ããããŒã泚æç¹ãªã©ã培åºè§£èª¬ïŒ ç§ãã¡ãžã§ãã¡ãã¬ãŒã®åç»éžèã§ã¯ãäºæ¥æããããããèšå®ãã質åã«å¯ŸããŠãæ±è·è
ãåçåç»ãæåºããããšãã§ããŸããäºæ¥æãæ±è·è
ããåç»ã§è³ªåã»åçãéãããšã§ãæžé¡ã ãã§ã¯äŒãããªãé°å²æ°ã匷ã¿ãçžæã«äŒããããšãã§ããŸãã WEB 颿¥ã»åç»éžèæ©èœã®ãªãªãŒã¹ã®ãç¥ãã åç»é
ä¿¡ãµãŒãã¹ã®èšèšãã€ã³ã Web ã¢ããªã§ãã®ãããªåç»é
ä¿¡ãµãŒãã¹ãéçºããå ŽåãããŠãŒã¶ã«ããåç»ã¢ããããŒãç°å¢ããšããŠãŒã¶ãžã®åç»ã®é
ä¿¡ã»åçç°å¢ããæäŸããå¿
èŠããããŸãã ãžã§ãã¡ãã¬ãŒã§æ±ãåç»ã¯äžè¬å
¬éããããã®ã§ã¯ãªããå
¬éæ¡ä»¶ãè€éã§ãã ãã£ãŠä»åã¯ããã®ãåç»ã¢ããããŒãïŒé
ä¿¡ç°å¢ããèªãµãŒãã¹å
ã«æ§ç¯ããæ¹éããšãã以äžã®ãããªåç»ãŸããã®èšèšãã€ã³ãã«ã€ããŠæ€èšã»æè¡éžå®ãè¡ãããšã«ããŸããã ïŒãã¡ãããèŠä»¶ã«ãã£ãŠã¯ YouTube ããæ³äººåãåç»é
ä¿¡ãã©ãããã©ãŒã ãå¥çŽããæ¹ãæè»œãªå ŽåãããããšæããŸãïŒ åç»ã®é²ç»ã»æ®åœ± ãµããŒããããåç»ãã¡ã€ã«ã®ãã©ãŒããããã©ãããã Web ã¢ããªå
ã«é²ç»æ©èœãèšããã åç»ã®ã¢ããããŒãïŒã¹ãã¬ãŒãžïŒ åç»ãã¡ã€ã«ã®ããªããŒã·ã§ã³ã§ãåç»ãã¡ã€ã«ã®è§£æããè¡ãã åç»ãã¡ã€ã«ã®ã¢ããããŒãå
ïŒã¹ãã¬ãŒãžïŒãã©ãã«ããã åç»ã®ãšã³ã³ãŒã åç»ãã¡ã€ã«ã®ãšã³ã³ãŒã圢åŒïŒH.264ãHLS çïŒãã©ãããã éåæãšã³ã³ãŒãã®å Žåãã¹ããŒã¿ã¹æ€ç¥ã»ãšã©ãŒãã³ããªã³ã°ãã©ãããã åç»ã®é
ä¿¡ïŒããŠã³ããŒãïŒ é
信圢åŒïŒããŠã³ããŒãïŒã¹ããªãŒãã³ã°ïŒãã©ãããã æå·åãããå Žåã埩å·ãã©ã®ããã«è¡ãã åç»ãã¡ã€ã«ã®å
¬éæ¹æ³ïŒã¢ã¯ã»ã¹å¶éïŒãã©ãããã åç»ã®åç Web ããŒãžäžã§åçãããã®ãããã®å Žåã®è¡šç€ºã»åçå¶åŸ¡ãã©ãããã ãã©ãŠã¶ãµããŒããã©ããŸã§ã«ããããé察å¿ã»ãšã©ãŒæã®å¶åŸ¡ãã©ãããã ä»åã¯ãäžèšã®å€ªåã§èšèŒãã ãåç»ã®ãšã³ã³ãŒããã« MediaConvert ãã ãåç»ã®åçãã« hls.js ãããããæ¡çšããŠããŸãã åé
ã®è©³çްã¯çããŸãããå
šäœãéããŠå€§ãŸãã«ã以äžã®ãããŒã§ãåç»ã¢ããããŒã â ãšã³ã³ãŒãïŒå€æïŒâ é
ä¿¡ã»åçããå®çŸããããšã«ããŸããã ãã©ãŠã¶ãã Ajax ã§åç»ã S3 ãžã¢ããããŒããã MediaConvert ãåç»ã HLS 圢åŒã«ãšã³ã³ãŒãïŒå€æïŒãã ãã©ãŠã¶ã§ hls.js ã䜿ãåç»ã CloudFront ããã¹ããªãŒãã³ã°åœ¢åŒã§åä¿¡ãåçãã ä»åã¯ãã®ãåç»ã¢ããããŒã â ãšã³ã³ãŒãïŒå€æïŒâ é
ä¿¡ã»åçãã«çŠç¹ãçµããMediaConvert ãš hls.js ãã©ã®ããã«äœ¿ã£ãã®ãã玹ä»ããŸãã MediaConvert ã«ãã HLS ãšã³ã³ãŒã AWS Elemental MediaConvert ã¯ãS3 ãšã®èŠªåæ§ãé«ããã¡ã€ã«ããŒã¹ã®åç»å€æãµãŒãã¹ã§ããèªåã§ ffmpeg ãªã©ã䜿ã£ãŠåç»ãšã³ã³ãŒããµãŒããæ§ç¯ã»ç®¡çããããšãªããã¹ã±ãŒã©ãã«ãªåç»å€æåŠçãæè»œã«ã·ã¹ãã ã«çµã¿èŸŒãããšãã§ããŸãã ref. AWS Elemental MediaConvert æéã¯åºåããåç»ã®åçæéã«å¿ããåŸé課éã§ããAWS ã³ã³ãœãŒã«ãã GUI ããŒã¹ã§ãšã³ã³ãŒãèšå®ãäœæãããããžã§ãïŒãšã³ã³ãŒãåŠçïŒãç»é²ããããšãã§ããŸãã ãŸããä» AWS ãµãŒãã¹åæ§ã« API ãæäŸãããŠãããAWS CLI ãåèšèªã® SDK ã䜿ã£ãŠããã°ã©ã ãããšã³ã³ãŒãåŠçãç»é²ããããšãã§ããã·ã¹ãã 飿ºã容æã§ãã # CLI ã§ãšã³ã³ãŒããžã§ããç»é²ããäŸ $ aws --endpoint-url https://abcd1234.mediaconvert.region-name-1.amazonaws.com --region region-name-1 mediaconvert create-job --cli-input-json file://~/job.json äžèš CLI ã³ãã³ãã§äžã®ãããªãšã³ã³ãŒãèšå®ãèšèŒãã JSON ã䜿ããžã§ããäœæãããšãS3 äžã®åç»ãã¡ã€ã«ããµã¯ããšãšã³ã³ãŒãããŠãããŸãããžã§ãã¯ãã¥ãŒã€ã³ã°ãããå
éšã§äžŠååŠçãããããã倧éã®ãšã³ã³ãŒãèŠæ±ã«ãç°¡åã«å¿ããããšãã§ããŸãã { ... "Settings" : { "Inputs" : [ { # å
¥åå
ã® S3 ãã±ããäžã®åç»ãã¡ã€ã« key ãæå® "FileInput" : "s3://testcontent/720/example_input_720p.mov" } ], "OutputGroups" : [ { "OutputGroupSettings" : { "FileGroupSettings" : { # åºåå
ã® S3 ãã±ãã key ãæå® "Destination" : "s3://testbucket/output" } }, # åç»ã»é³å£°ã®ãšã³ã³ãŒãèšå®ãæå® # ããã§å質ã¬ãã«æ¯ã«æ¯ãåããè€æ°ã®ãã¡ã€ã«ãåºåããã # ãµã ãã€ã« jpg ãäœæãããããããšãå¯èœ "Outputs" : [ { "VideoDescription" : { ⊠}, "AudioDescriptions" : { ⊠} } ] } ] } } ref. AWSCLI ã䜿çšãã AWSElemental MediaConvertCreateJob ã®äŸ ãšã³ã³ãŒããå®äºãããžã§ãã¯ãcron + SDK ãªã©ã§ API ãä»ããŠå®æãã§ãã¯ããä»ã«ãCloudWatch Events ã«ããã€ãã³ãç£èŠ â Lambda ã§åŠçãããããªããšãã§ããŸãã ref. AWS Elemental MediaConvert ã«ãã CloudWatch ã€ãã³ã ã®äœ¿çš ãªãåç»ãåãšã³ã³ãŒãããã®ã éåžžããŠãŒã¶ããã¢ããããŒããããåç»ãã¡ã€ã«ã¯ãæ¢ã«äœããã®ã³ãŒããã¯ã§å§çž®ãã .mp4 ã .mov ãªã©ã®ã³ã³ãããã©ãŒãããã«å€æãããŠããããšãæ®ã©ã§ãã ããã Web ããŒãžã§ <video> ã¿ã°ã䜿ããããåç»ãã¡ã€ã«ãåçããããšããå Žåã ãåç»ãã©ãŒãããã«ãã©ãŠã¶ãé察å¿ã ãšåçã§ããªãã ãšããç°å¢äŸååé¡ããããŸãã ãã©ãŠã¶ãšåç»ãã©ãŒãããã®ãµããŒã衚 ref. HTML5 video > Browser support ãã®åé¡ã«å¯Ÿå¿ãããããå€ãã®åç»é
ä¿¡ãµãŒãã¹ã§ã¯ããŠãŒã¶ã®åç»ãå€ãã®ç°å¢ã§åçå¯èœãª MP4 ã³ã³ãããã©ãŒãããïŒH.264 + AAC ã³ãŒããã¯ïŒãªã©ã®åœ¢åŒãžãåãšã³ã³ãŒããããŠããŸãã ãžã§ãã¡ãã¬ãŒã®åç»éžèã§ã¯äžèšç®çã«å ããŠãåç»é²èЧæã®åç·ã»ç«¯æ«è² è·ãæãã ãHTTP ã¹ããªãŒãã³ã°åœ¢åŒã ã§åç»ãé
ä¿¡ããããã«ãã¢ããããŒããããåç»ãå
šãŠ HLS åœ¢åŒ ã«ãšã³ã³ãŒãããŠããŸãã HLS - HTTP Live Streaming åœ¢åŒ HLS 㯠HTTP Live Streaming ã®ç¥ã§ãApple 瀟ã®éçºããèŠæ Œã§ããHTTP ããŒã¹ã®ã¹ããªãŒãã³ã°éä¿¡ãããã³ã«ã§ã现åãã«ãã MP4 åç»ãã¡ã€ã«ãåå²ããŠã³ããŒããããããšã§åç»ã®ã¹ããªãŒãã³ã°é
ä¿¡ãå®çŸããŠããŸãã HLS 圢åŒã«ãšã³ã³ãŒããããåç»ã¯ .ts ãšããåå²ãããã¡ãã£ã¢ãã¡ã€ã«çŸ€ãšã .m3u8 ãšãããã¡ãã£ã¢ãã¡ã€ã«ã®ååŸå
ãç§æ°ãªã©ãèšèŒããããã¹ããã¡ã€ã«ã§æ§æãããŸãã .m3u8 ãã¡ã€ã«ã®äŸïŒãããã§ã¹ããã¡ã€ã«ããã¬ã€ãªã¹ããã¡ã€ã«ãšãïŒ #EXTM3U #EXT-X-TARGETDURATION:10 #EXT-X-VERSION:3 #EXT-X-MEDIA-SEQUENCE:0 #EXT-X-PLAYLIST-TYPE:VOD #EXTINF:9.97663, media-0.ts #EXTINF:9.97663, media-1.ts #EXTINF:7.10710, media-2.ts #EXT-X-ENDLIST ref. RFC 8216: HTTP Live Streaming HLS ã¯ä»ã®ã¹ããªãŒãã³ã°åœ¢åŒãšæ¯èŒããŠãã©ã€ãé
ä¿¡ / VOD ã©ã¡ãã«ã察å¿å¯èœãªããšã察å¿ãã©ãŠã¶ãå€ãããšãå°çšã®é
ä¿¡ãµãŒãã䜿ããã«é
ä¿¡å¯èœãªããšãªã©ãããè¿å¹Žã®åç»é
ä¿¡ãµãŒãã¹ã§åºãå©çšãããŠããŸãã Web ãšã³ãžãã¢ã®èŠç¹ããèŠãŠãã HTTP ããŒã¹ãªãããã£ãã·ã¥ã HTTPS æå·åãªã©ãæ¢å Web æè¡ãšæãåãããããšãæ³åãããããæ±ããããå°è±¡ã§ããã MediaConvert ã® HLS ãšã³ã³ãŒããžã§ãèšå® å®éã«ããã°ã©ã ãã API çµç±ã§ HLS ãšã³ã³ãŒããžã§ããç»é²ããéã®èšå® JSON ã¯ã以äžã®ããã« GUI ã§ãžã§ããã³ãã¬ãŒããäœæããŠç¢ºèªããããšãã§ããŸãã ãã®ã JSON ã衚瀺ãã§ãåè¿°ãã CLI ã³ãã³ã mediaconvert create-job --cli-input-json ã«æž¡ãã JSON ã衚瀺ãããŸããå®è£
ã®éã«ã¯ãã¡ããåèã«ããªããã ãŠãŒã¶ãŒã¬ã€ã ãåç
§ããŠå©çšãããæ©èœã«ããããèšå®ã远å ããŠããããšãããããããŸãã æ³šæç¹ã»ã€ãŸã¥ãããã€ã³ã å©çšåã« IAM ã§ MediaConvert çšããŒã«ã®èšå®ãå¿
èŠã§ã ã¹ããã 3. IAM æš©éã®èšå® AWS ã³ã³ãœãŒã«ã® Service Quotas > AWS ãµãŒãã¹ > AWS Elemental MediaConvert ãã確èªã§ããŸããããšã³ã³ãŒã䞊è¡åŠçã®åæå®è¡æ°äžé㯠20 ã«ãªã£ãŠããŸã AWS ã«ãŒãã¢ã«ãŠã³ã 1 ã€ã«ã€ã 1 ãµãŒãã¹ãå²åœãŠãããã®ã§ããããå¢ããããå Žåã¯ç³è«ãå¿
èŠã§ã ãšã³ã³ãŒããžã§ãããã¥ãŒã€ã³ã°ããããã¥ãŒããäœæããŠããžã§ãã®ç»é²æã«éžã¹ãã®ã§ãããäžèšããã䞊è¡åŠçã®åæå®è¡æ°äžéãã¯ãã®ããã¥ãŒãæ¯ã«åçã«æ¯ãåããããŸã äŸãã°ãæ¬çªãã¥ãŒããšãæ€èšŒãã¥ãŒãã® 2 ã€ã®ãã¥ãŒãäœæããå Žåãããããã®äžŠè¡åŠçã®åæå®è¡æ°äžé㯠10 ãã€ã«ãªãã®ã§æ³šæããŠãã ãã ãããã§ã¹ãæé圢åŒïŒManifest duration formatïŒã«æŽæ°ïŒINTEGERïŒãæå®ããŠãããšãiOS Safari ã§ãåç»ã®å®éã®åçæéãšãåçãã¬ã€ã€ãŒã®ã·ãŒã¯ããŒã«è¡šç€ºãããåèšæéã«ãºã¬ãçãããåé¡ããããŸãã æµ®åå°æ°ç¹ïŒFLOATING POINTïŒã«å€æŽããããšã§å¯Ÿå¿ããŸããããããã§ã¹ããã¡ã€ã«ã«åºåãããå .ts ãã¡ã€ã«ã®é·ãããæµ®åå°æ°ç¹ â æŽæ°ã«å€æããåãäžããããããšã§ãºã¬ãçããŠããããã§ãã hls.js ã«ãã HLS åç»ã®åçå¶åŸ¡ MediaConvert ã«ãã HLS 圢åŒã«ãšã³ã³ãŒããããåç»ããWeb ãã©ãŠã¶ã§åçããããã«å¿
èŠãªã®ããhls.js ã§ãã ref. video-dev/hls.js å®ã¯ HLS ã«ããã¹ããªãŒãã³ã°é
ä¿¡ã¯ãçŸç¶ Safari ãªã©éããããã©ãŠã¶ã§ãããã€ãã£ãã§ãµããŒããããŠããŸããã ref. https://caniuse.com/http-live-streaming ãã® HLS åç»ã Safari 以å€ã® Google Chrome ã IE11 ãªã©ã®äž»èŠãã©ãŠã¶ã§åçå¯èœã«ãããããhls.js ãå©çšãããŠããŸããå
éšçã«ã¯ãé察å¿ãã©ãŠã¶ç°å¢ã«ãããŠããã©ãŠã¶ã® MediaSource æ¡åŒµ ã䜿ã£ãŠ HLS åç»ãåçãã仿§ã«ãªã£ãŠããŸãã Video.js ãšã®æ¯èŒ 䌌ããããªã©ã€ãã©ãªã« Video.js ãšãããã®ããããå°å
¥ãè¿·ã£ãã®ã§ãã ⊠Video.js 㯠UI ãã»ããã«ãªã£ãã HLS ã«å¯Ÿå¿ããåçãã¬ã€ã€ãŒãã©ã€ãã©ãª HLS 察å¿ä»¥å€ã«ããåå¹ãç« åããªã©æ©èœãè±å¯ hls.js ã¯ãã©ãŠã¶æšæºã® <video> ã¿ã°ã§ HLS ã«å¯Ÿå¿ããããšã ããç®çã«ããã HLS ã¯ã©ã€ã¢ã³ããã©ã€ãã©ãª UI ãªã©ã¯ãªããåç»åçãã¬ã€ã€ãŒã¯ãã©ãŠã¶æšæºã®ãŸãŸ âŠãšãäžèšã®ããã« hls.js ã®æ¹ãã·ã³ãã«ã«ããããããšãå®çŸã§ãããããä»å㯠hls.js ãæ¡çšããŸããã GitHub ã®ã¹ã¿ãŒæ°ã¯å
çºã® Video.js ã®æ¹ãå€ãã®ã§ãããhls.js ãéçºã¯æŽ»çºã§ãæ¥æ¬ã§ã¯ ã°ãã·ãŒ ãããäžççã«ã¯ TED ã Twitter ã§ãæ¡çšãããŠãããååå®çžŸãããããšæããŸãã hls.js ã«ããå®è£
åºæ¬çã«ã¯ README ã® Getting Started ã®éãã§å®è£
ã§ããŸããäžéš README ã®ãµã³ãã«ã³ãŒãããæç²ããŠè§£èª¬ãããšâŠ var video = document . getElementById ( "video" ); var videoSrc = "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8" ; if ( Hls . isSupported ()) { var hls = new Hls (); hls . loadSource ( videoSrc ); hls . attachMedia ( video ); hls . on ( Hls . Events . MANIFEST_PARSED , function () { video . play (); }); } äžèš Hls.isSupported() ã®åå²ã§ãHLS ããã€ãã£ããµããŒãããŠããªããã©ãŠã¶ã®åŠçãå®è£
ããŠããŸãã æ¬æ¥ <video> ã® src 屿§ã«ã»ããããã¹ã .m3u8 ãã¡ã€ã«ã® URL ãž hls.loadSource() ã§ã¢ã¯ã»ã¹ãããã¯ã©ã€ã¢ã³ããã XHR ãªã¯ãšã¹ããé£ã°ããŸãããã®åŸ hls.attachMedia() ã§ã€ã³ã¹ã¿ã³ã¹ã DOM äžã® <video> ã¿ã°ã«çŽã¥ããŠããŸãã else if ( video . canPlayType ( 'application/vnd.apple.mpegurl' )) { video . src = videoSrc ; video . addEventListener ( 'loadedmetadata' , function () { video . play (); }); } äžèšã®åå²ã iOS Safari ãªã©ãHLS åç»ããã€ãã£ããµããŒãããŠãããã©ãŠã¶åãã®åŠçã§ããåçŽã« .m3u8 ãžã® URL ã <video> ã¿ã°ã® src ãžä»äžããŠããã ãã§ããã ïŒãµã³ãã«ã³ãŒãã§ã¯ããããã§ã¹ããã¡ã€ã«ã®ããŒãåŸã«èªååçãããããã«ãªã£ãŠããããã§ãïŒ æ³šæç¹ã»ã€ãŸã¥ãããã€ã³ã hls.js ã¯ã©ã€ã¢ã³ããååŸãã HLS åç»ãã¡ã€ã«çŸ€ã¯ãCORS ãããã§ GET ãªã¯ãšã¹ããèš±å¯ãããç°å¢ã«èšçœ®ããå¿
èŠããããŸã .m3u8 ãããã§ã¹ããã¡ã€ã«ãã¢ããªã® API ãªã©ããè¿åŽããå ŽåãContent-Type ã application/x-mpegURL ã«ããŠæž¡ãå¿
èŠããããŸã iOS Safari ãªã©ã® hls.js é察å¿ãã©ãŠã¶åãã®å®è£
ãæèããå¿
èŠããããŸã hls.js ã«ããå¶åŸ¡ãè€éã«ãªãã±ãŒã¹ã§ã¯ãåããããªå¶åŸ¡ã hls.js é察å¿ãã©ãŠã¶åãã«å®è£
ã§ãããïŒãã€ã¡ãŒãžã§ããªããšææ»ããçºçãããã§ã ãã®ä»ãããã³ããšã³ãã§ã¯ <video> ã¿ã°ã®ãã©ãŠã¶æ¯ã®æåãã衚瀺ã®éãã«æéãããããŸãããïŒããçšåºŠäºæ³ã¯ããŠããŸãããããã¯ãã¡ãã£ã¢ã®åãæ±ãã¯é£ããâŠïŒ hls.js èªäœã¯å°å
¥ãæè»œã§ããµã¯ããš HLS åç»ã®ãã«ããã©ãŠã¶å¯Ÿå¿ãå®çŸã§ãããšãŠã䜿ããããã£ãã§ãã@types ãååšããã®ã§ãTypeScript ç°å¢ã§ãé£ãªãå®è£
ã§ããŸããã SSR ã HLS + AES-128 ã®åçã«ã察å¿ããŠããã®ã§ãèå³ã®ããæ¹ã¯äžåºŠ å
¬åŒããã¥ã¡ã³ã ã確èªããŠã¿ãŠãã ããã ãããã« åŸæ¥ãåç»é
ä¿¡ãµãŒãã¹ãæ§ç¯ããå Žåãffmpeg ãèŒãããšã³ã³ãŒããµãŒãããã¹ããªãŒãã³ã°é
ä¿¡ãµãŒããå¥å»ºãŠããŠãè² è·ã«å¿ããŠã¹ã±ãŒã«ãããŠâŠã®ãããªèšèšãå¿
èŠã ã£ãããšæããŸãã ä»åãMediaConvert ãã¯ãããšãã AWS ãµãŒãã¹ãš hls.js ãå©çšããããšã§ãæè»œã«ãã¹ã±ãŒã©ãã«ãªåç»ãšã³ã³ãŒãïŒHTTP ã¹ããªãŒãã³ã°é
ä¿¡ç°å¢ãæ§ç¯ããããšãã§ããŸããã ãžã§ãã¡ãã¬ãŒã®åç»éžèã¯ãŸã ãªãªãŒã¹ããã°ããã§ãã®ã§ãä»åŸåé¿ãèŠãªããããããªãæ¹åãéããŠãããããšæããŸããæåŸãŸã§ãèªã¿ããã ãããããšãããããŸããã åéã®äžèЧ | æ ªåŒäŒç€Ÿã¡ãã¬ãŒ ã¡ãã¬ãŒã®æ¡çšæ
å ±ã¯ãã¡ãããã確èªãã ããã www.medley.jp
æ ªåŒäŒç€Ÿã¡ãã¬ãŒã®ãšã³ãžãã¢ã®ç¬¹å¡ã§ãã ç§ãéçºãæ
åœããŠãããžã§ãã¡ãã¬ãŒã§ãå
æ 10 æ 23 æ¥ã« WEB 颿¥ã»åç»éžèããªãªãŒã¹ããŸããã job-medley.com WEB 颿¥ãåç»éžèãšãã«ãæšä»ã®é察é¢ã§ã®å°±è·æŽ»åããŒãºã«å¿ããã¹ãéçºããŸããã ãªãªãŒã¹ã¯ 2 ã€ã®æ©èœãåæã§ãããä»å㯠WEB 颿¥ã®è£åŽã«çµã£ãŠã玹ä»ããŸãã WEB 颿¥æŠèŠ WEB 颿¥ãšã¯ããªã¢ã«ã¿ã€ã ã§äºæ¥è
æ§ãšæ±è·è
æ§ãããªã³ã©ã€ã³é¢æ¥ãè¡ãããšãã§ããæ©èœã§ãã å°çšã®ã¢ããªã±ãŒã·ã§ã³ã¯å¿
èŠãªããPCãã¹ããŒããã©ã³ã®ãã©ãŠã¶ããå©çšã§ããŸãã ãµãŒãã¹éžå® éçºã«ããããããã€ãã®åè£ãããããŸããããæçµçã«ã¯èªç€Ÿå
ã§ãå°å
¥å®çžŸã®ãã SkyWay ã䜿çšããããšã«ããŸããã SkyWay ãšã¯ WebRTCïŒWeb Real Time CommunicationïŒã䜿çšãããªã³ã©ã€ã³ã®ãããªé話ãããµãŒãã¹ã«å°å
¥ã§ãããã«ããã©ãããã©ãŒã SDK ã§ãã 2020 幎 11 ææç¹ã§ãJavaScript SDKãiOS SDKãAndroid SDK ãæäŸãããŠããŸãã ã·ã°ããªã³ã°ãµãŒããªã©ã® WebRTC ã«å¿
èŠãšãªãã€ã³ãã©æ§ç¯ãäžèŠã§ã 䜿çšäžéã€ãã®ç¡æãã©ã³ããããŸã NTT ã³ãã¥ãã±ãŒã·ã§ã³ãºãéçºããŠããŸã webrtc.ecl.ntt.com WEB 颿¥ã®å¯Ÿå¿ãã©ãŠã¶ããŒãžã§ã³ïŒ2020 幎 11 ææç¹ïŒ ãã©ãããã©ãŒã 察å¿ããŒãžã§ã³ PC: Google Chrome ããŒãžã§ã³ 84 ä»¥äž PC: Microsoft Edge ããŒãžã§ã³ 84 ä»¥äž iOS: Safari iOS12 ä»¥äž Android: Google Chrome ããŒãžã§ã³ 85 ä»¥äž Andoid9 ä»¥äž SkyWay JavaScript SDK ã®åäœç¢ºèªãã©ãŠã¶ãWebRTC ã®å¯Ÿå¿ç¶æ³ãå©çšè
ã®å©çšåŸåãã察å¿ãã©ãŠã¶ã®ããŒãžã§ã³ãäžèšã®ããã«èšå®ããŸããã webrtc.ecl.ntt.com SkyWay ã®æ¥ç¶ã¢ãã« SkyWay ã§ãããªé話ãå®è£
ããå Žåã2 çš®é¡ã®æ¥ç¶ã¢ãã«ããéžã³ãŸãã SkyWay é»è©±ã¢ãã« é»è©±ã®ããã« 1 察 1 ã§ã®ãããªéè©±ãæ³å®ããã¢ãã«ã§ãã Peer ã€ã³ã¹ã¿ã³ã¹(ã·ã°ããªã³ã°ãµãŒãã«ãã£ãŠçºè¡ãããäžæã® PeerID ãæã€)åå£«ã§æ¥ç¶ããŸãã æ¥ç¶ããããã«ã¯ãçžæã® PeerID ãå¿
èŠã«ãªããŸãã SkyWay ã«ãŒã ã¢ãã« åäžã«ãŒã å
ã®å
šãŠã® Peer ã§ãããªé話ããã¢ãã«ã§ãã ã«ãŒã åã䜿çšããŠåå ããŸããçžæã® PeerID ãç¥ãå¿
èŠã¯ãããŸããã ã«ãŒã å㯠API ããŒæ¯ã«ç¬ç«ããŠããŸãã ã«ãŒã ã®æ¥ç¶ã¿ã€ãã¯ãã«ã¡ãã·ã¥ã SFU ã® 2 çš®é¡ããéžã¹ãŸãã åå è
å
šå¡ãžã®ãã£ãããªã©ã®ããŒã¿éä¿¡ãã§ããŸãã ãžã§ãã¡ãã¬ãŒã® WEB 颿¥ã§ã¯ã ä»åŸã®æ©èœæ¡åŒµãæ³å®ããŠããã¡ãã®ã«ãŒã ã¢ãã«ãæ¡çšããŸããã ã«ãŒã ã®éä¿¡ã¿ã€ã ã«ãŒã ã«è€æ°äººãåå ããŠããå Žåãããããã®éä¿¡ãã©ãè¡ããããã¡ãã·ã¥ãš SFU ããéžã¶ããšãã§ããŸãã ãã«ã¡ãã·ã¥ ç»ååŒçšå
: https://webrtc.ecl.ntt.com/skyway/overview.html#_4-sfu%E3%82%B5%E3%83%BC%E3%83%90 å
šå¡ãçžäºã«éä¿¡ãè¡ããŸãã人æ°ãå¢ãããšã人æ°å端æ«ã®ãšã³ã³ãŒãè² è·ãšéä¿¡éãå¢å ããŸãã SFU SFU ã®å Žåãäžãã®æ¥ç¶ã¯ 1 æ¬ã«ãªãã®ã§ãã¡ãã·ã¥ããã端æ«ã®ãšã³ã³ãŒãè² è·ããéä¿¡éã®è»œæžãæåŸ
ã§ããŸãã ç»ååŒçšå
: https://webrtc.ecl.ntt.com/skyway/overview.html#_4-sfu%E3%82%B5%E3%83%BC%E3%83%90 éä¿¡æ¹åŒã®éã㯠SkyWay ãé èœããŠãããã®ã§ãjoinRoom æã® mode ã mesh ãã sfu ã«å€æŽããã ãã§åãæ¿ãããŸãã peer . joinRoom ( roomName , { mode: "mesh ã sfu ãæå®" , stream: mediaStream }); webrtc.ecl.ntt.com ãžã§ãã¡ãã¬ãŒã® WEB 颿¥ã§ã¯ã颿¥åå 人æ°ãèæ
®ã㊠mesh ã䜿çšããŠããŸãã å®è£
ã€ã¡ãŒãž const peer = new Peer ( peer_id , { key: api_key , }); peer . once ( "open" , () => { room . once ( "open" , () => { // ã«ãŒã åå åŸã«çºçããã€ãã³ã }); room . on ( "peerJoin" , ( peerId : string ) => { // ã«ãŒã ã«èª°ãåå ããå Žåã«çºçããã€ãã³ã }); room . on ( "stream" , ( stream : RoomStream ) => { // stream ãåããå Žåã«çºçããã€ãã³ã }); room . on ( "data" , ({ src , data }) => { // data ãåããå Žåã«çºçããã€ãã³ã }); }); peer . on ( "error" , ( error : Error ) => { // ãšã©ãŒçºçæã«çºçããã€ãã³ã }); peer . joinRoom ( roomName , { mode: "mesh" , stream: mediaStream }); room . close (); peer . disconnect (); Peer ãäœæã peer.joinRoom() ã§ã«ãŒã ã«åå room.stream ã€ãã³ãã§ä»ã®åå è
ã® stream ãåãåã room.data() ã§ãã£ãããªã©ãããŒã¿éä¿¡ãã§ãã room.close() ã§ã«ãŒã ããéåº ã SkyWay ã® JavaScript SDK ã䜿çšããåºæ¬çãªå®è£
ã«ãªããŸãã ãã®å®è£
ã« navigator.mediaDevices.getUserMedia() ã§ååŸãã stream ã joinRoom ã§æž¡ã steam ã€ãã³ãã§åãåã£ã stream ã video ã§åçãã ã远å ããã°ããªã³ã©ã€ã³ã§ã®ãããªé話ãå¯èœã«ãªããŸãã ã¹ããŒããã©ã³å¯Ÿå¿ PC ãšåæ§ã®ã³ãŒãã§ã»ãŒåäœããŸããããiOS ã Android 端æ«ã§ãæ©çš®äŸåãšæãããæåã®èª¿æ»ãšå¯Ÿå¿ã«æéãããããŸããããã®äžéšã玹ä»ããŸãã ã¿ããç§»åãããšæ åãæ ããªã çºçããåé¡ iOS12ãiOS13 ãªã©ã§ è€æ°ã®ã¿ããã²ãããå Žåã«ãæ åã®ååŸãã§ããªããªã ã¹ã¯ãªãŒã³ããã¯ããã®åŸ©åž°æã«ãæ åãååŸã§ããªããªã ãšããåé¡ãèµ·ããŸãããiOS14 ã§ã¯ã¿ãåãæ¿ãæã«æ åãååŸã§ããããã«ãªã£ãŠããŸããããã¹ã¯ãªãŒã³ããã¯ããã®åŸ©åž°æã¯æ åãååŸã§ããªããŸãŸã§ããã å¯Ÿå¿ ãã®å¯Ÿå¿ã¯ visibilitychange ã€ãã³ãã§ãã¿ãã®åãæ¿ããšãã¹ã¯ãªãŒã³ããã¯ããã®åŸ©åž°æã®ã€ãã³ããæŸã ååŸæžã® stream ã® track ã stop ãã stream ãåãçŽã SkyWay ã® room.replaceStream() ã§ãWebRTC ã§äœ¿çšããŠãã stream ãå·®ãæ¿ãã 以äžã®å®è£
ã«ããã察å¿ããŸããã __ document . addEventListener ( "visibilitychange" , async () => { if ( document . visibilityState !== "visible" ) { return ; } localMedia . getTracks (). forEach (( track : MediaStreamTrack ) => { track . stop (); }); const replaceStream = await navigator . mediaDevices . getUserMedia ({ video: true , audio: true , }); room . replaceStream ( replaceStream ); }); ã€ã€ãã³ã®æäœã§æ åãæ¢ãŸã çºçããåé¡ iOS12ãiOS13 ã§ã¯èµ·ãããŸããã§ããããiOS14 ã§ãªã³ã©ã€ã³é¢æ¥éäžã«ã€ã€ãã³ãã¹ããŒããã©ã³ããå€ããšãçžæåŽã®æ åãæ¢ãŸãããã«ãªããŸããã å¯Ÿå¿ ãããŸã§ã¯ãçžæã®æ åãšé³å£°ãåçããããã«ãvideo ã¿ã°ã« stream ãæž¡ããŠæ åãšé³å£°ãåçããŠããŸããã video : mute ã«ããŠæ åãåç audio : é³å£°ãåç ãšãé³å£°ãšåçãåãããšãããã€ã€ãã³ãå€ããŠã忢ããããšã¯ãªããªããŸããã å¶çŽèšå® getUserMedia ã§ååŸãã MediaStream ã¯ãå¶çŽãèšå®ããããšã§ããã€ã¹ã®æ¶è²»ãªãœãŒã¹ãæããããšãã§ããŸãã èšå®äŸ: navigator . mediaDevices . getUserMedia ({ audio: true , video: true }); â navigator . mediaDevices . getUserMedia ({ audio: true , video: { frameRate: 15 } }); å¶çŽãèšå®å¯èœãã©ããã®ç¢ºèª: getSupportedConstraints() ã§ã察å¿ããŠããå¶çŽåãååŸããããšãã§ããŸãã const supportedConstraints = navigator . mediaDevices . getSupportedConstraints (); 䜿çšããŠãããã©ãŠã¶ããã®å¶çŽã«å¯Ÿå¿ããŠãããã確èªãã察å¿ããŠããå Žåã®ã¿èšå®ãæå¹ã«ããŸãã äŸãã°ãã¹ããŒããã©ã³ã®å Žåã«ä»¥äžã®èšå®ãããã°ãã€ã³ã«ã¡ã©ã䜿çšãããã¬ãŒã ã¬ãŒãã 20 ã«æãã320x320 ã®è§£å床ã«å¶éããããšãã§ããŸãã const options = { facingMode: "user" , frameRate: 20 , width: 320 , height: 320 }; æå®ããå¶éãå¿
ã䜿çšãããä¿èšŒã¯ãªããæ©çš®äŸåã®åœ±é¿ãåããèšå®ã§ãããã®ã§ã察象ãšããŠããç°å¢ã«ããããŠæ€èšŒãšèª¿æŽãããå¿
èŠãããç¹ã«ã¯æ³šæãå¿
èŠã§ãã åäœæ€èšŒäžã«ãæ©çš®äŸåãšæãããæåãããäŸã玹ä»ããŸãã ãªãã·ã§ã³ æå frameRate æå®ãããšäžéš Android 端æ«ã§ä»¥äžã®æåãããã 1. Android 端æ«ã§é¢æ¥ã«åå ãã 2. iOS Safari ã§åå ãã 3. Android åŽã®æ åãšé³å£°ã Safari ã«éãããªã Safari ã§å
ã«åå ããå Žåã«ã¯åé¡ããªãã è§£å床æå® äŸ: width: 320, height: 320 frameRate ã ãæå®ããŠãããšãã«äžèšã®æåããã AndroidãiOS Safari ã®çµã¿åããã§åé¡ãèµ·ãããªããªã£ãã è§£å床ãæå®ãããšã€ã³ã«ã¡ã©ã§ã¯ãªãããªã¢ã«ã¡ã©ã䜿ã Android 端æ«ããã£ãã å¶çŽèšå®ã«ã€ããŠã¯ãçŸåšã調æŽäžã§ãã åèæ
å ± SkyWay Conference conf.webrtc.ecl.ntt.com github.com SkyWay ã®ã«ãŒã æ©èœã䜿çšãããã¢ç°å¢ã§ãã GitHub ã«ã³ãŒããå
¬éãããŠããã®ã§ãã«ãŒã ã䜿çšããå Žåã®æåãšå®è£
æ¹æ³ã確èªã§ããŸãã éçºãããŠãããšãå®è£
ã®åé¡ãªã®ããSkyWay ã® SDK ã®ä»æ§ãªã®ããç¹å®ã®ããã€ã¹ã§èµ·ããåé¡ãªã®ãã®åãåãã«æéããããããããã¡ãã®ç°å¢ãåèã«ãªããŸããã ãŸãšã SkyWay ã WebRTC ãšã°ã«ãŒãã§ã®ãããªé話ã®å®è£
ãçµ±åããŠæäŸããŠããããããéçºæã¯ãèªç€ŸãµãŒãã¹ãšããŠã® WEB 颿¥ã®æ©èœã«éäžããããšãã§ããŸããã ã¹ããŒããã©ã³ã®ãã©ãŠã¶å¯Ÿå¿ãšèª¿æ»ã«æéããããããšããããŸãããä»åŸãå©çšè
ããã®ãã£ãŒãããã¯ãåŸãªããæ¹åããŠãããããšæããŸãã ã¡ãã¬ãŒã§ã¯ãããŒãºã«ããããæ°æ©èœã®éçºã«ãåãå
¥ããŠããŸããå€ãã®å©çšè
ã«å®éã«äœ¿ããããµãŒãã¹ã®éçºãããŠã¿ãããšæã£ãæ¹ããã²ãæ°è»œã«ã話ããŸãããïŒ www.medley.jp
æ ªåŒäŒç€Ÿã¡ãã¬ãŒã®ãšã³ãžãã¢ã®ç¬¹å¡ã§ãã ç§ãéçºãæ
åœããŠãããžã§ãã¡ãã¬ãŒã§ãå
æ 10 æ 23 æ¥ã« WEB 颿¥ã»åç»éžèããªãªãŒã¹ããŸããã job-medley.com WEB 颿¥ãåç»éžèãšãã«ãæšä»ã®é察é¢ã§ã®å°±è·æŽ»åããŒãºã«å¿ããã¹ãéçºããŸããã ãªãªãŒã¹ã¯ 2 ã€ã®æ©èœãåæã§ãããä»å㯠WEB 颿¥ã®è£åŽã«çµã£ãŠã玹ä»ããŸãã WEB 颿¥æŠèŠ WEB 颿¥ãšã¯ããªã¢ã«ã¿ã€ã ã§äºæ¥è
æ§ãšæ±è·è
æ§ãããªã³ã©ã€ã³é¢æ¥ãè¡ãããšãã§ããæ©èœã§ãã å°çšã®ã¢ããªã±ãŒã·ã§ã³ã¯å¿
èŠãªããPCãã¹ããŒããã©ã³ã®ãã©ãŠã¶ããå©çšã§ããŸãã ãµãŒãã¹éžå® éçºã«ããããããã€ãã®åè£ãããããŸããããæçµçã«ã¯èªç€Ÿå
ã§ãå°å
¥å®çžŸã®ãã SkyWay ã䜿çšããããšã«ããŸããã SkyWay ãšã¯ WebRTCïŒWeb Real Time CommunicationïŒã䜿çšãããªã³ã©ã€ã³ã®ãããªé話ãããµãŒãã¹ã«å°å
¥ã§ãããã«ããã©ãããã©ãŒã SDK ã§ãã 2020 幎 11 ææç¹ã§ãJavaScript SDKãiOS SDKãAndroid SDK ãæäŸãããŠããŸãã ã·ã°ããªã³ã°ãµãŒããªã©ã® WebRTC ã«å¿
èŠãšãªãã€ã³ãã©æ§ç¯ãäžèŠã§ã 䜿çšäžéã€ãã®ç¡æãã©ã³ããããŸã NTT ã³ãã¥ãã±ãŒã·ã§ã³ãºãéçºããŠããŸã webrtc.ecl.ntt.com WEB 颿¥ã®å¯Ÿå¿ãã©ãŠã¶ããŒãžã§ã³ïŒ2020 幎 11 ææç¹ïŒ ãã©ãããã©ãŒã 察å¿ããŒãžã§ã³ PC: Google Chrome ããŒãžã§ã³ 84 ä»¥äž PC: Microsoft Edge ããŒãžã§ã³ 84 ä»¥äž iOS: Safari iOS12 ä»¥äž Android: Google Chrome ããŒãžã§ã³ 85 ä»¥äž Andoid9 ä»¥äž SkyWay JavaScript SDK ã®åäœç¢ºèªãã©ãŠã¶ãWebRTC ã®å¯Ÿå¿ç¶æ³ãå©çšè
ã®å©çšåŸåãã察å¿ãã©ãŠã¶ã®ããŒãžã§ã³ãäžèšã®ããã«èšå®ããŸããã webrtc.ecl.ntt.com SkyWay ã®æ¥ç¶ã¢ãã« SkyWay ã§ãããªé話ãå®è£
ããå Žåã2 çš®é¡ã®æ¥ç¶ã¢ãã«ããéžã³ãŸãã SkyWay é»è©±ã¢ãã« é»è©±ã®ããã« 1 察 1 ã§ã®ãããªéè©±ãæ³å®ããã¢ãã«ã§ãã Peer ã€ã³ã¹ã¿ã³ã¹(ã·ã°ããªã³ã°ãµãŒãã«ãã£ãŠçºè¡ãããäžæã® PeerID ãæã€)åå£«ã§æ¥ç¶ããŸãã æ¥ç¶ããããã«ã¯ãçžæã® PeerID ãå¿
èŠã«ãªããŸãã SkyWay ã«ãŒã ã¢ãã« åäžã«ãŒã å
ã®å
šãŠã® Peer ã§ãããªé話ããã¢ãã«ã§ãã ã«ãŒã åã䜿çšããŠåå ããŸããçžæã® PeerID ãç¥ãå¿
èŠã¯ãããŸããã ã«ãŒã å㯠API ããŒæ¯ã«ç¬ç«ããŠããŸãã ã«ãŒã ã®æ¥ç¶ã¿ã€ãã¯ãã«ã¡ãã·ã¥ã SFU ã® 2 çš®é¡ããéžã¹ãŸãã åå è
å
šå¡ãžã®ãã£ãããªã©ã®ããŒã¿éä¿¡ãã§ããŸãã ãžã§ãã¡ãã¬ãŒã® WEB 颿¥ã§ã¯ã ä»åŸã®æ©èœæ¡åŒµãæ³å®ããŠããã¡ãã®ã«ãŒã ã¢ãã«ãæ¡çšããŸããã ã«ãŒã ã®éä¿¡ã¿ã€ã ã«ãŒã ã«è€æ°äººãåå ããŠããå Žåãããããã®éä¿¡ãã©ãè¡ããããã¡ãã·ã¥ãš SFU ããéžã¶ããšãã§ããŸãã ãã«ã¡ãã·ã¥ ç»ååŒçšå
: https://webrtc.ecl.ntt.com/skyway/overview.html#_4-sfu%E3%82%B5%E3%83%BC%E3%83%90 å
šå¡ãçžäºã«éä¿¡ãè¡ããŸãã人æ°ãå¢ãããšã人æ°å端æ«ã®ãšã³ã³ãŒãè² è·ãšéä¿¡éãå¢å ããŸãã SFU SFU ã®å Žåãäžãã®æ¥ç¶ã¯ 1 æ¬ã«ãªãã®ã§ãã¡ãã·ã¥ããã端æ«ã®ãšã³ã³ãŒãè² è·ããéä¿¡éã®è»œæžãæåŸ
ã§ããŸãã ç»ååŒçšå
: https://webrtc.ecl.ntt.com/skyway/overview.html#_4-sfu%E3%82%B5%E3%83%BC%E3%83%90 éä¿¡æ¹åŒã®éã㯠SkyWay ãé èœããŠãããã®ã§ãjoinRoom æã® mode ã mesh ãã sfu ã«å€æŽããã ãã§åãæ¿ãããŸãã peer . joinRoom ( roomName , { mode: "mesh ã sfu ãæå®" , stream: mediaStream }); webrtc.ecl.ntt.com ãžã§ãã¡ãã¬ãŒã® WEB 颿¥ã§ã¯ã颿¥åå 人æ°ãèæ
®ã㊠mesh ã䜿çšããŠããŸãã å®è£
ã€ã¡ãŒãž const peer = new Peer ( peer_id , { key: api_key , }); peer . once ( "open" , () => { room . once ( "open" , () => { // ã«ãŒã åå åŸã«çºçããã€ãã³ã }); room . on ( "peerJoin" , ( peerId : string ) => { // ã«ãŒã ã«èª°ãåå ããå Žåã«çºçããã€ãã³ã }); room . on ( "stream" , ( stream : RoomStream ) => { // stream ãåããå Žåã«çºçããã€ãã³ã }); room . on ( "data" , ({ src , data }) => { // data ãåããå Žåã«çºçããã€ãã³ã }); }); peer . on ( "error" , ( error : Error ) => { // ãšã©ãŒçºçæã«çºçããã€ãã³ã }); peer . joinRoom ( roomName , { mode: "mesh" , stream: mediaStream }); room . close (); peer . disconnect (); Peer ãäœæã peer.joinRoom() ã§ã«ãŒã ã«åå room.stream ã€ãã³ãã§ä»ã®åå è
ã® stream ãåãåã room.data() ã§ãã£ãããªã©ãããŒã¿éä¿¡ãã§ãã room.close() ã§ã«ãŒã ããéåº ã SkyWay ã® JavaScript SDK ã䜿çšããåºæ¬çãªå®è£
ã«ãªããŸãã ãã®å®è£
ã« navigator.mediaDevices.getUserMedia() ã§ååŸãã stream ã joinRoom ã§æž¡ã steam ã€ãã³ãã§åãåã£ã stream ã video ã§åçãã ã远å ããã°ããªã³ã©ã€ã³ã§ã®ãããªé話ãå¯èœã«ãªããŸãã ã¹ããŒããã©ã³å¯Ÿå¿ PC ãšåæ§ã®ã³ãŒãã§ã»ãŒåäœããŸããããiOS ã Android 端æ«ã§ãæ©çš®äŸåãšæãããæåã®èª¿æ»ãšå¯Ÿå¿ã«æéãããããŸããããã®äžéšã玹ä»ããŸãã ã¿ããç§»åãããšæ åãæ ããªã çºçããåé¡ iOS12ãiOS13 ãªã©ã§ è€æ°ã®ã¿ããã²ãããå Žåã«ãæ åã®ååŸãã§ããªããªã ã¹ã¯ãªãŒã³ããã¯ããã®åŸ©åž°æã«ãæ åãååŸã§ããªããªã ãšããåé¡ãèµ·ããŸãããiOS14 ã§ã¯ã¿ãåãæ¿ãæã«æ åãååŸã§ããããã«ãªã£ãŠããŸããããã¹ã¯ãªãŒã³ããã¯ããã®åŸ©åž°æã¯æ åãååŸã§ããªããŸãŸã§ããã å¯Ÿå¿ ãã®å¯Ÿå¿ã¯ visibilitychange ã€ãã³ãã§ãã¿ãã®åãæ¿ããšãã¹ã¯ãªãŒã³ããã¯ããã®åŸ©åž°æã®ã€ãã³ããæŸã ååŸæžã® stream ã® track ã stop ãã stream ãåãçŽã SkyWay ã® room.replaceStream() ã§ãWebRTC ã§äœ¿çšããŠãã stream ãå·®ãæ¿ãã 以äžã®å®è£
ã«ããã察å¿ããŸããã __ document . addEventListener ( "visibilitychange" , async () => { if ( document . visibilityState !== "visible" ) { return ; } localMedia . getTracks (). forEach (( track : MediaStreamTrack ) => { track . stop (); }); const replaceStream = await navigator . mediaDevices . getUserMedia ({ video: true , audio: true , }); room . replaceStream ( replaceStream ); }); ã€ã€ãã³ã®æäœã§æ åãæ¢ãŸã çºçããåé¡ iOS12ãiOS13 ã§ã¯èµ·ãããŸããã§ããããiOS14 ã§ãªã³ã©ã€ã³é¢æ¥éäžã«ã€ã€ãã³ãã¹ããŒããã©ã³ããå€ããšãçžæåŽã®æ åãæ¢ãŸãããã«ãªããŸããã å¯Ÿå¿ ãããŸã§ã¯ãçžæã®æ åãšé³å£°ãåçããããã«ãvideo ã¿ã°ã« stream ãæž¡ããŠæ åãšé³å£°ãåçããŠããŸããã video : mute ã«ããŠæ åãåç audio : é³å£°ãåç ãšãé³å£°ãšåçãåãããšãããã€ã€ãã³ãå€ããŠã忢ããããšã¯ãªããªããŸããã å¶çŽèšå® getUserMedia ã§ååŸãã MediaStream ã¯ãå¶çŽãèšå®ããããšã§ããã€ã¹ã®æ¶è²»ãªãœãŒã¹ãæããããšãã§ããŸãã èšå®äŸ: navigator . mediaDevices . getUserMedia ({ audio: true , video: true }); â navigator . mediaDevices . getUserMedia ({ audio: true , video: { frameRate: 15 } }); å¶çŽãèšå®å¯èœãã©ããã®ç¢ºèª: getSupportedConstraints() ã§ã察å¿ããŠããå¶çŽåãååŸããããšãã§ããŸãã const supportedConstraints = navigator . mediaDevices . getSupportedConstraints (); 䜿çšããŠãããã©ãŠã¶ããã®å¶çŽã«å¯Ÿå¿ããŠãããã確èªãã察å¿ããŠããå Žåã®ã¿èšå®ãæå¹ã«ããŸãã äŸãã°ãã¹ããŒããã©ã³ã®å Žåã«ä»¥äžã®èšå®ãããã°ãã€ã³ã«ã¡ã©ã䜿çšãããã¬ãŒã ã¬ãŒãã 20 ã«æãã320x320 ã®è§£å床ã«å¶éããããšãã§ããŸãã const options = { facingMode: "user" , frameRate: 20 , width: 320 , height: 320 }; æå®ããå¶éãå¿
ã䜿çšãããä¿èšŒã¯ãªããæ©çš®äŸåã®åœ±é¿ãåããèšå®ã§ãããã®ã§ã察象ãšããŠããç°å¢ã«ããããŠæ€èšŒãšèª¿æŽãããå¿
èŠãããç¹ã«ã¯æ³šæãå¿
èŠã§ãã åäœæ€èšŒäžã«ãæ©çš®äŸåãšæãããæåãããäŸã玹ä»ããŸãã ãªãã·ã§ã³ æå frameRate æå®ãããšäžéš Android 端æ«ã§ä»¥äžã®æåãããã 1. Android 端æ«ã§é¢æ¥ã«åå ãã 2. iOS Safari ã§åå ãã 3. Android åŽã®æ åãšé³å£°ã Safari ã«éãããªã Safari ã§å
ã«åå ããå Žåã«ã¯åé¡ããªãã è§£å床æå® äŸ: width: 320, height: 320 frameRate ã ãæå®ããŠãããšãã«äžèšã®æåããã AndroidãiOS Safari ã®çµã¿åããã§åé¡ãèµ·ãããªããªã£ãã è§£å床ãæå®ãããšã€ã³ã«ã¡ã©ã§ã¯ãªãããªã¢ã«ã¡ã©ã䜿ã Android 端æ«ããã£ãã å¶çŽèšå®ã«ã€ããŠã¯ãçŸåšã調æŽäžã§ãã åèæ
å ± SkyWay Conference conf.webrtc.ecl.ntt.com github.com SkyWay ã®ã«ãŒã æ©èœã䜿çšãããã¢ç°å¢ã§ãã GitHub ã«ã³ãŒããå
¬éãããŠããã®ã§ãã«ãŒã ã䜿çšããå Žåã®æåãšå®è£
æ¹æ³ã確èªã§ããŸãã éçºãããŠãããšãå®è£
ã®åé¡ãªã®ããSkyWay ã® SDK ã®ä»æ§ãªã®ããç¹å®ã®ããã€ã¹ã§èµ·ããåé¡ãªã®ãã®åãåãã«æéããããããããã¡ãã®ç°å¢ãåèã«ãªããŸããã ãŸãšã SkyWay ã WebRTC ãšã°ã«ãŒãã§ã®ãããªé話ã®å®è£
ãçµ±åããŠæäŸããŠããããããéçºæã¯ãèªç€ŸãµãŒãã¹ãšããŠã® WEB 颿¥ã®æ©èœã«éäžããããšãã§ããŸããã ã¹ããŒããã©ã³ã®ãã©ãŠã¶å¯Ÿå¿ãšèª¿æ»ã«æéããããããšããããŸãããä»åŸãå©çšè
ããã®ãã£ãŒãããã¯ãåŸãªããæ¹åããŠãããããšæããŸãã ã¡ãã¬ãŒã§ã¯ãããŒãºã«ããããæ°æ©èœã®éçºã«ãåãå
¥ããŠããŸããå€ãã®å©çšè
ã«å®éã«äœ¿ããããµãŒãã¹ã®éçºãããŠã¿ãããšæã£ãæ¹ããã²ãæ°è»œã«ã話ããŸãããïŒ www.medley.jp
æ ªåŒäŒç€Ÿã¡ãã¬ãŒã®ãšã³ãžãã¢ã®ç¬¹å¡ã§ãã ç§ãéçºãæ
åœããŠãããžã§ãã¡ãã¬ãŒã§ãå
æ 10 æ 23 æ¥ã« WEB 颿¥ã»åç»éžèããªãªãŒã¹ããŸããã job-medley.com WEB 颿¥ãåç»éžèãšãã«ãæšä»ã®é察é¢ã§ã®å°±è·æŽ»åããŒãºã«å¿ããã¹ãéçºããŸããã ãªãªãŒã¹ã¯ 2 ã€ã®æ©èœãåæã§ãããä»å㯠WEB 颿¥ã®è£åŽã«çµã£ãŠã玹ä»ããŸãã WEB 颿¥æŠèŠ WEB 颿¥ãšã¯ããªã¢ã«ã¿ã€ã ã§äºæ¥è
æ§ãšæ±è·è
æ§ãããªã³ã©ã€ã³é¢æ¥ãè¡ãããšãã§ããæ©èœã§ãã å°çšã®ã¢ããªã±ãŒã·ã§ã³ã¯å¿
èŠãªããPCãã¹ããŒããã©ã³ã®ãã©ãŠã¶ããå©çšã§ããŸãã ãµãŒãã¹éžå® éçºã«ããããããã€ãã®åè£ãããããŸããããæçµçã«ã¯èªç€Ÿå
ã§ãå°å
¥å®çžŸã®ãã SkyWay ã䜿çšããããšã«ããŸããã SkyWay ãšã¯ WebRTCïŒWeb Real Time CommunicationïŒã䜿çšãããªã³ã©ã€ã³ã®ãããªé話ãããµãŒãã¹ã«å°å
¥ã§ãããã«ããã©ãããã©ãŒã SDK ã§ãã 2020 幎 11 ææç¹ã§ãJavaScript SDKãiOS SDKãAndroid SDK ãæäŸãããŠããŸãã ã·ã°ããªã³ã°ãµãŒããªã©ã® WebRTC ã«å¿
èŠãšãªãã€ã³ãã©æ§ç¯ãäžèŠã§ã 䜿çšäžéã€ãã®ç¡æãã©ã³ããããŸã NTT ã³ãã¥ãã±ãŒã·ã§ã³ãºãéçºããŠããŸã webrtc.ecl.ntt.com WEB 颿¥ã®å¯Ÿå¿ãã©ãŠã¶ããŒãžã§ã³ïŒ2020 幎 11 ææç¹ïŒ ãã©ãããã©ãŒã 察å¿ããŒãžã§ã³ PC: Google Chrome ããŒãžã§ã³ 84 ä»¥äž PC: Microsoft Edge ããŒãžã§ã³ 84 ä»¥äž iOS: Safari iOS12 ä»¥äž Android: Google Chrome ããŒãžã§ã³ 85 ä»¥äž Andoid9 ä»¥äž SkyWay JavaScript SDK ã®åäœç¢ºèªãã©ãŠã¶ãWebRTC ã®å¯Ÿå¿ç¶æ³ãå©çšè
ã®å©çšåŸåãã察å¿ãã©ãŠã¶ã®ããŒãžã§ã³ãäžèšã®ããã«èšå®ããŸããã webrtc.ecl.ntt.com SkyWay ã®æ¥ç¶ã¢ãã« SkyWay ã§ãããªé話ãå®è£
ããå Žåã2 çš®é¡ã®æ¥ç¶ã¢ãã«ããéžã³ãŸãã SkyWay é»è©±ã¢ãã« é»è©±ã®ããã« 1 察 1 ã§ã®ãããªéè©±ãæ³å®ããã¢ãã«ã§ãã Peer ã€ã³ã¹ã¿ã³ã¹(ã·ã°ããªã³ã°ãµãŒãã«ãã£ãŠçºè¡ãããäžæã® PeerID ãæã€)åå£«ã§æ¥ç¶ããŸãã æ¥ç¶ããããã«ã¯ãçžæã® PeerID ãå¿
èŠã«ãªããŸãã SkyWay ã«ãŒã ã¢ãã« åäžã«ãŒã å
ã®å
šãŠã® Peer ã§ãããªé話ããã¢ãã«ã§ãã ã«ãŒã åã䜿çšããŠåå ããŸããçžæã® PeerID ãç¥ãå¿
èŠã¯ãããŸããã ã«ãŒã å㯠API ããŒæ¯ã«ç¬ç«ããŠããŸãã ã«ãŒã ã®æ¥ç¶ã¿ã€ãã¯ãã«ã¡ãã·ã¥ã SFU ã® 2 çš®é¡ããéžã¹ãŸãã åå è
å
šå¡ãžã®ãã£ãããªã©ã®ããŒã¿éä¿¡ãã§ããŸãã ãžã§ãã¡ãã¬ãŒã® WEB 颿¥ã§ã¯ã ä»åŸã®æ©èœæ¡åŒµãæ³å®ããŠããã¡ãã®ã«ãŒã ã¢ãã«ãæ¡çšããŸããã ã«ãŒã ã®éä¿¡ã¿ã€ã ã«ãŒã ã«è€æ°äººãåå ããŠããå Žåãããããã®éä¿¡ãã©ãè¡ããããã¡ãã·ã¥ãš SFU ããéžã¶ããšãã§ããŸãã ãã«ã¡ãã·ã¥ ç»ååŒçšå
: https://webrtc.ecl.ntt.com/skyway/overview.html#_4-sfu%E3%82%B5%E3%83%BC%E3%83%90 å
šå¡ãçžäºã«éä¿¡ãè¡ããŸãã人æ°ãå¢ãããšã人æ°å端æ«ã®ãšã³ã³ãŒãè² è·ãšéä¿¡éãå¢å ããŸãã SFU SFU ã®å Žåãäžãã®æ¥ç¶ã¯ 1 æ¬ã«ãªãã®ã§ãã¡ãã·ã¥ããã端æ«ã®ãšã³ã³ãŒãè² è·ããéä¿¡éã®è»œæžãæåŸ
ã§ããŸãã ç»ååŒçšå
: https://webrtc.ecl.ntt.com/skyway/overview.html#_4-sfu%E3%82%B5%E3%83%BC%E3%83%90 éä¿¡æ¹åŒã®éã㯠SkyWay ãé èœããŠãããã®ã§ãjoinRoom æã® mode ã mesh ãã sfu ã«å€æŽããã ãã§åãæ¿ãããŸãã peer . joinRoom ( roomName , { mode: "mesh ã sfu ãæå®" , stream: mediaStream }); webrtc.ecl.ntt.com ãžã§ãã¡ãã¬ãŒã® WEB 颿¥ã§ã¯ã颿¥åå 人æ°ãèæ
®ã㊠mesh ã䜿çšããŠããŸãã å®è£
ã€ã¡ãŒãž const peer = new Peer ( peer_id , { key: api_key , }); peer . once ( "open" , () => { room . once ( "open" , () => { // ã«ãŒã åå åŸã«çºçããã€ãã³ã }); room . on ( "peerJoin" , ( peerId : string ) => { // ã«ãŒã ã«èª°ãåå ããå Žåã«çºçããã€ãã³ã }); room . on ( "stream" , ( stream : RoomStream ) => { // stream ãåããå Žåã«çºçããã€ãã³ã }); room . on ( "data" , ({ src , data }) => { // data ãåããå Žåã«çºçããã€ãã³ã }); }); peer . on ( "error" , ( error : Error ) => { // ãšã©ãŒçºçæã«çºçããã€ãã³ã }); peer . joinRoom ( roomName , { mode: "mesh" , stream: mediaStream }); room . close (); peer . disconnect (); Peer ãäœæã peer.joinRoom() ã§ã«ãŒã ã«åå room.stream ã€ãã³ãã§ä»ã®åå è
ã® stream ãåãåã room.data() ã§ãã£ãããªã©ãããŒã¿éä¿¡ãã§ãã room.close() ã§ã«ãŒã ããéåº ã SkyWay ã® JavaScript SDK ã䜿çšããåºæ¬çãªå®è£
ã«ãªããŸãã ãã®å®è£
ã« navigator.mediaDevices.getUserMedia() ã§ååŸãã stream ã joinRoom ã§æž¡ã steam ã€ãã³ãã§åãåã£ã stream ã video ã§åçãã ã远å ããã°ããªã³ã©ã€ã³ã§ã®ãããªé話ãå¯èœã«ãªããŸãã ã¹ããŒããã©ã³å¯Ÿå¿ PC ãšåæ§ã®ã³ãŒãã§ã»ãŒåäœããŸããããiOS ã Android 端æ«ã§ãæ©çš®äŸåãšæãããæåã®èª¿æ»ãšå¯Ÿå¿ã«æéãããããŸããããã®äžéšã玹ä»ããŸãã ã¿ããç§»åãããšæ åãæ ããªã çºçããåé¡ iOS12ãiOS13 ãªã©ã§ è€æ°ã®ã¿ããã²ãããå Žåã«ãæ åã®ååŸãã§ããªããªã ã¹ã¯ãªãŒã³ããã¯ããã®åŸ©åž°æã«ãæ åãååŸã§ããªããªã ãšããåé¡ãèµ·ããŸãããiOS14 ã§ã¯ã¿ãåãæ¿ãæã«æ åãååŸã§ããããã«ãªã£ãŠããŸããããã¹ã¯ãªãŒã³ããã¯ããã®åŸ©åž°æã¯æ åãååŸã§ããªããŸãŸã§ããã å¯Ÿå¿ ãã®å¯Ÿå¿ã¯ visibilitychange ã€ãã³ãã§ãã¿ãã®åãæ¿ããšãã¹ã¯ãªãŒã³ããã¯ããã®åŸ©åž°æã®ã€ãã³ããæŸã ååŸæžã® stream ã® track ã stop ãã stream ãåãçŽã SkyWay ã® room.replaceStream() ã§ãWebRTC ã§äœ¿çšããŠãã stream ãå·®ãæ¿ãã 以äžã®å®è£
ã«ããã察å¿ããŸããã __ document . addEventListener ( "visibilitychange" , async () => { if ( document . visibilityState !== "visible" ) { return ; } localMedia . getTracks (). forEach (( track : MediaStreamTrack ) => { track . stop (); }); const replaceStream = await navigator . mediaDevices . getUserMedia ({ video: true , audio: true , }); room . replaceStream ( replaceStream ); }); ã€ã€ãã³ã®æäœã§æ åãæ¢ãŸã çºçããåé¡ iOS12ãiOS13 ã§ã¯èµ·ãããŸããã§ããããiOS14 ã§ãªã³ã©ã€ã³é¢æ¥éäžã«ã€ã€ãã³ãã¹ããŒããã©ã³ããå€ããšãçžæåŽã®æ åãæ¢ãŸãããã«ãªããŸããã å¯Ÿå¿ ãããŸã§ã¯ãçžæã®æ åãšé³å£°ãåçããããã«ãvideo ã¿ã°ã« stream ãæž¡ããŠæ åãšé³å£°ãåçããŠããŸããã video : mute ã«ããŠæ åãåç audio : é³å£°ãåç ãšãé³å£°ãšåçãåãããšãããã€ã€ãã³ãå€ããŠã忢ããããšã¯ãªããªããŸããã å¶çŽèšå® getUserMedia ã§ååŸãã MediaStream ã¯ãå¶çŽãèšå®ããããšã§ããã€ã¹ã®æ¶è²»ãªãœãŒã¹ãæããããšãã§ããŸãã èšå®äŸ: navigator . mediaDevices . getUserMedia ({ audio: true , video: true }); â navigator . mediaDevices . getUserMedia ({ audio: true , video: { frameRate: 15 } }); å¶çŽãèšå®å¯èœãã©ããã®ç¢ºèª: getSupportedConstraints() ã§ã察å¿ããŠããå¶çŽåãååŸããããšãã§ããŸãã const supportedConstraints = navigator . mediaDevices . getSupportedConstraints (); 䜿çšããŠãããã©ãŠã¶ããã®å¶çŽã«å¯Ÿå¿ããŠãããã確èªãã察å¿ããŠããå Žåã®ã¿èšå®ãæå¹ã«ããŸãã äŸãã°ãã¹ããŒããã©ã³ã®å Žåã«ä»¥äžã®èšå®ãããã°ãã€ã³ã«ã¡ã©ã䜿çšãããã¬ãŒã ã¬ãŒãã 20 ã«æãã320x320 ã®è§£å床ã«å¶éããããšãã§ããŸãã const options = { facingMode: "user" , frameRate: 20 , width: 320 , height: 320 }; æå®ããå¶éãå¿
ã䜿çšãããä¿èšŒã¯ãªããæ©çš®äŸåã®åœ±é¿ãåããèšå®ã§ãããã®ã§ã察象ãšããŠããç°å¢ã«ããããŠæ€èšŒãšèª¿æŽãããå¿
èŠãããç¹ã«ã¯æ³šæãå¿
èŠã§ãã åäœæ€èšŒäžã«ãæ©çš®äŸåãšæãããæåãããäŸã玹ä»ããŸãã ãªãã·ã§ã³ æå frameRate æå®ãããšäžéš Android 端æ«ã§ä»¥äžã®æåãããã 1. Android 端æ«ã§é¢æ¥ã«åå ãã 2. iOS Safari ã§åå ãã 3. Android åŽã®æ åãšé³å£°ã Safari ã«éãããªã Safari ã§å
ã«åå ããå Žåã«ã¯åé¡ããªãã è§£å床æå® äŸ: width: 320, height: 320 frameRate ã ãæå®ããŠãããšãã«äžèšã®æåããã AndroidãiOS Safari ã®çµã¿åããã§åé¡ãèµ·ãããªããªã£ãã è§£å床ãæå®ãããšã€ã³ã«ã¡ã©ã§ã¯ãªãããªã¢ã«ã¡ã©ã䜿ã Android 端æ«ããã£ãã å¶çŽèšå®ã«ã€ããŠã¯ãçŸåšã調æŽäžã§ãã åèæ
å ± SkyWay Conference conf.webrtc.ecl.ntt.com github.com SkyWay ã®ã«ãŒã æ©èœã䜿çšãããã¢ç°å¢ã§ãã GitHub ã«ã³ãŒããå
¬éãããŠããã®ã§ãã«ãŒã ã䜿çšããå Žåã®æåãšå®è£
æ¹æ³ã確èªã§ããŸãã éçºãããŠãããšãå®è£
ã®åé¡ãªã®ããSkyWay ã® SDK ã®ä»æ§ãªã®ããç¹å®ã®ããã€ã¹ã§èµ·ããåé¡ãªã®ãã®åãåãã«æéããããããããã¡ãã®ç°å¢ãåèã«ãªããŸããã ãŸãšã SkyWay ã WebRTC ãšã°ã«ãŒãã§ã®ãããªé話ã®å®è£
ãçµ±åããŠæäŸããŠããããããéçºæã¯ãèªç€ŸãµãŒãã¹ãšããŠã® WEB 颿¥ã®æ©èœã«éäžããããšãã§ããŸããã ã¹ããŒããã©ã³ã®ãã©ãŠã¶å¯Ÿå¿ãšèª¿æ»ã«æéããããããšããããŸãããä»åŸãå©çšè
ããã®ãã£ãŒãããã¯ãåŸãªããæ¹åããŠãããããšæããŸãã ã¡ãã¬ãŒã§ã¯ãããŒãºã«ããããæ°æ©èœã®éçºã«ãåãå
¥ããŠããŸããå€ãã®å©çšè
ã«å®éã«äœ¿ããããµãŒãã¹ã®éçºãããŠã¿ãããšæã£ãæ¹ããã²ãæ°è»œã«ã話ããŸãããïŒ www.medley.jp
æ ªåŒäŒç€Ÿã¡ãã¬ãŒã®ãšã³ãžãã¢ã®ç¬¹å¡ã§ãã ç§ãéçºãæ
åœããŠãããžã§ãã¡ãã¬ãŒã§ãå
æ 10 æ 23 æ¥ã« WEB 颿¥ã»åç»éžèããªãªãŒã¹ããŸããã job-medley.com WEB 颿¥ãåç»éžèãšãã«ãæšä»ã®é察é¢ã§ã®å°±è·æŽ»åããŒãºã«å¿ããã¹ãéçºããŸããã ãªãªãŒã¹ã¯ 2 ã€ã®æ©èœãåæã§ãããä»å㯠WEB 颿¥ã®è£åŽã«çµã£ãŠã玹ä»ããŸãã WEB 颿¥æŠèŠ WEB 颿¥ãšã¯ããªã¢ã«ã¿ã€ã ã§äºæ¥è
æ§ãšæ±è·è
æ§ãããªã³ã©ã€ã³é¢æ¥ãè¡ãããšãã§ããæ©èœã§ãã å°çšã®ã¢ããªã±ãŒã·ã§ã³ã¯å¿
èŠãªããPCãã¹ããŒããã©ã³ã®ãã©ãŠã¶ããå©çšã§ããŸãã ãµãŒãã¹éžå® éçºã«ããããããã€ãã®åè£ãããããŸããããæçµçã«ã¯èªç€Ÿå
ã§ãå°å
¥å®çžŸã®ãã SkyWay ã䜿çšããããšã«ããŸããã SkyWay ãšã¯ WebRTCïŒWeb Real Time CommunicationïŒã䜿çšãããªã³ã©ã€ã³ã®ãããªé話ãããµãŒãã¹ã«å°å
¥ã§ãããã«ããã©ãããã©ãŒã SDK ã§ãã 2020 幎 11 ææç¹ã§ãJavaScript SDKãiOS SDKãAndroid SDK ãæäŸãããŠããŸãã ã·ã°ããªã³ã°ãµãŒããªã©ã® WebRTC ã«å¿
èŠãšãªãã€ã³ãã©æ§ç¯ãäžèŠã§ã 䜿çšäžéã€ãã®ç¡æãã©ã³ããããŸã NTT ã³ãã¥ãã±ãŒã·ã§ã³ãºãéçºããŠããŸã webrtc.ecl.ntt.com WEB 颿¥ã®å¯Ÿå¿ãã©ãŠã¶ããŒãžã§ã³ïŒ2020 幎 11 ææç¹ïŒ ãã©ãããã©ãŒã 察å¿ããŒãžã§ã³ PC: Google Chrome ããŒãžã§ã³ 84 ä»¥äž PC: Microsoft Edge ããŒãžã§ã³ 84 ä»¥äž iOS: Safari iOS12 ä»¥äž Android: Google Chrome ããŒãžã§ã³ 85 ä»¥äž Andoid9 ä»¥äž SkyWay JavaScript SDK ã®åäœç¢ºèªãã©ãŠã¶ãWebRTC ã®å¯Ÿå¿ç¶æ³ãå©çšè
ã®å©çšåŸåãã察å¿ãã©ãŠã¶ã®ããŒãžã§ã³ãäžèšã®ããã«èšå®ããŸããã webrtc.ecl.ntt.com SkyWay ã®æ¥ç¶ã¢ãã« SkyWay ã§ãããªé話ãå®è£
ããå Žåã2 çš®é¡ã®æ¥ç¶ã¢ãã«ããéžã³ãŸãã SkyWay é»è©±ã¢ãã« é»è©±ã®ããã« 1 察 1 ã§ã®ãããªéè©±ãæ³å®ããã¢ãã«ã§ãã Peer ã€ã³ã¹ã¿ã³ã¹(ã·ã°ããªã³ã°ãµãŒãã«ãã£ãŠçºè¡ãããäžæã® PeerID ãæã€)åå£«ã§æ¥ç¶ããŸãã æ¥ç¶ããããã«ã¯ãçžæã® PeerID ãå¿
èŠã«ãªããŸãã SkyWay ã«ãŒã ã¢ãã« åäžã«ãŒã å
ã®å
šãŠã® Peer ã§ãããªé話ããã¢ãã«ã§ãã ã«ãŒã åã䜿çšããŠåå ããŸããçžæã® PeerID ãç¥ãå¿
èŠã¯ãããŸããã ã«ãŒã å㯠API ããŒæ¯ã«ç¬ç«ããŠããŸãã ã«ãŒã ã®æ¥ç¶ã¿ã€ãã¯ãã«ã¡ãã·ã¥ã SFU ã® 2 çš®é¡ããéžã¹ãŸãã åå è
å
šå¡ãžã®ãã£ãããªã©ã®ããŒã¿éä¿¡ãã§ããŸãã ãžã§ãã¡ãã¬ãŒã® WEB 颿¥ã§ã¯ã ä»åŸã®æ©èœæ¡åŒµãæ³å®ããŠããã¡ãã®ã«ãŒã ã¢ãã«ãæ¡çšããŸããã ã«ãŒã ã®éä¿¡ã¿ã€ã ã«ãŒã ã«è€æ°äººãåå ããŠããå Žåãããããã®éä¿¡ãã©ãè¡ããããã¡ãã·ã¥ãš SFU ããéžã¶ããšãã§ããŸãã ãã«ã¡ãã·ã¥ ç»ååŒçšå
: https://webrtc.ecl.ntt.com/skyway/overview.html#_4-sfu%E3%82%B5%E3%83%BC%E3%83%90 å
šå¡ãçžäºã«éä¿¡ãè¡ããŸãã人æ°ãå¢ãããšã人æ°å端æ«ã®ãšã³ã³ãŒãè² è·ãšéä¿¡éãå¢å ããŸãã SFU SFU ã®å Žåãäžãã®æ¥ç¶ã¯ 1 æ¬ã«ãªãã®ã§ãã¡ãã·ã¥ããã端æ«ã®ãšã³ã³ãŒãè² è·ããéä¿¡éã®è»œæžãæåŸ
ã§ããŸãã ç»ååŒçšå
: https://webrtc.ecl.ntt.com/skyway/overview.html#_4-sfu%E3%82%B5%E3%83%BC%E3%83%90 éä¿¡æ¹åŒã®éã㯠SkyWay ãé èœããŠãããã®ã§ãjoinRoom æã® mode ã mesh ãã sfu ã«å€æŽããã ãã§åãæ¿ãããŸãã peer . joinRoom ( roomName , { mode: "mesh ã sfu ãæå®" , stream: mediaStream }); webrtc.ecl.ntt.com ãžã§ãã¡ãã¬ãŒã® WEB 颿¥ã§ã¯ã颿¥åå 人æ°ãèæ
®ã㊠mesh ã䜿çšããŠããŸãã å®è£
ã€ã¡ãŒãž const peer = new Peer ( peer_id , { key: api_key , }); peer . once ( "open" , () => { room . once ( "open" , () => { // ã«ãŒã åå åŸã«çºçããã€ãã³ã }); room . on ( "peerJoin" , ( peerId : string ) => { // ã«ãŒã ã«èª°ãåå ããå Žåã«çºçããã€ãã³ã }); room . on ( "stream" , ( stream : RoomStream ) => { // stream ãåããå Žåã«çºçããã€ãã³ã }); room . on ( "data" , ({ src , data }) => { // data ãåããå Žåã«çºçããã€ãã³ã }); }); peer . on ( "error" , ( error : Error ) => { // ãšã©ãŒçºçæã«çºçããã€ãã³ã }); peer . joinRoom ( roomName , { mode: "mesh" , stream: mediaStream }); room . close (); peer . disconnect (); Peer ãäœæã peer.joinRoom() ã§ã«ãŒã ã«åå room.stream ã€ãã³ãã§ä»ã®åå è
ã® stream ãåãåã room.data() ã§ãã£ãããªã©ãããŒã¿éä¿¡ãã§ãã room.close() ã§ã«ãŒã ããéåº ã SkyWay ã® JavaScript SDK ã䜿çšããåºæ¬çãªå®è£
ã«ãªããŸãã ãã®å®è£
ã« navigator.mediaDevices.getUserMedia() ã§ååŸãã stream ã joinRoom ã§æž¡ã steam ã€ãã³ãã§åãåã£ã stream ã video ã§åçãã ã远å ããã°ããªã³ã©ã€ã³ã§ã®ãããªé話ãå¯èœã«ãªããŸãã ã¹ããŒããã©ã³å¯Ÿå¿ PC ãšåæ§ã®ã³ãŒãã§ã»ãŒåäœããŸããããiOS ã Android 端æ«ã§ãæ©çš®äŸåãšæãããæåã®èª¿æ»ãšå¯Ÿå¿ã«æéãããããŸããããã®äžéšã玹ä»ããŸãã ã¿ããç§»åãããšæ åãæ ããªã çºçããåé¡ iOS12ãiOS13 ãªã©ã§ è€æ°ã®ã¿ããã²ãããå Žåã«ãæ åã®ååŸãã§ããªããªã ã¹ã¯ãªãŒã³ããã¯ããã®åŸ©åž°æã«ãæ åãååŸã§ããªããªã ãšããåé¡ãèµ·ããŸãããiOS14 ã§ã¯ã¿ãåãæ¿ãæã«æ åãååŸã§ããããã«ãªã£ãŠããŸããããã¹ã¯ãªãŒã³ããã¯ããã®åŸ©åž°æã¯æ åãååŸã§ããªããŸãŸã§ããã å¯Ÿå¿ ãã®å¯Ÿå¿ã¯ visibilitychange ã€ãã³ãã§ãã¿ãã®åãæ¿ããšãã¹ã¯ãªãŒã³ããã¯ããã®åŸ©åž°æã®ã€ãã³ããæŸã ååŸæžã® stream ã® track ã stop ãã stream ãåãçŽã SkyWay ã® room.replaceStream() ã§ãWebRTC ã§äœ¿çšããŠãã stream ãå·®ãæ¿ãã 以äžã®å®è£
ã«ããã察å¿ããŸããã __ document . addEventListener ( "visibilitychange" , async () => { if ( document . visibilityState !== "visible" ) { return ; } localMedia . getTracks (). forEach (( track : MediaStreamTrack ) => { track . stop (); }); const replaceStream = await navigator . mediaDevices . getUserMedia ({ video: true , audio: true , }); room . replaceStream ( replaceStream ); }); ã€ã€ãã³ã®æäœã§æ åãæ¢ãŸã çºçããåé¡ iOS12ãiOS13 ã§ã¯èµ·ãããŸããã§ããããiOS14 ã§ãªã³ã©ã€ã³é¢æ¥éäžã«ã€ã€ãã³ãã¹ããŒããã©ã³ããå€ããšãçžæåŽã®æ åãæ¢ãŸãããã«ãªããŸããã å¯Ÿå¿ ãããŸã§ã¯ãçžæã®æ åãšé³å£°ãåçããããã«ãvideo ã¿ã°ã« stream ãæž¡ããŠæ åãšé³å£°ãåçããŠããŸããã video : mute ã«ããŠæ åãåç audio : é³å£°ãåç ãšãé³å£°ãšåçãåãããšãããã€ã€ãã³ãå€ããŠã忢ããããšã¯ãªããªããŸããã å¶çŽèšå® getUserMedia ã§ååŸãã MediaStream ã¯ãå¶çŽãèšå®ããããšã§ããã€ã¹ã®æ¶è²»ãªãœãŒã¹ãæããããšãã§ããŸãã èšå®äŸ: navigator . mediaDevices . getUserMedia ({ audio: true , video: true }); â navigator . mediaDevices . getUserMedia ({ audio: true , video: { frameRate: 15 } }); å¶çŽãèšå®å¯èœãã©ããã®ç¢ºèª: getSupportedConstraints() ã§ã察å¿ããŠããå¶çŽåãååŸããããšãã§ããŸãã const supportedConstraints = navigator . mediaDevices . getSupportedConstraints (); 䜿çšããŠãããã©ãŠã¶ããã®å¶çŽã«å¯Ÿå¿ããŠãããã確èªãã察å¿ããŠããå Žåã®ã¿èšå®ãæå¹ã«ããŸãã äŸãã°ãã¹ããŒããã©ã³ã®å Žåã«ä»¥äžã®èšå®ãããã°ãã€ã³ã«ã¡ã©ã䜿çšãããã¬ãŒã ã¬ãŒãã 20 ã«æãã320x320 ã®è§£å床ã«å¶éããããšãã§ããŸãã const options = { facingMode: "user" , frameRate: 20 , width: 320 , height: 320 }; æå®ããå¶éãå¿
ã䜿çšãããä¿èšŒã¯ãªããæ©çš®äŸåã®åœ±é¿ãåããèšå®ã§ãããã®ã§ã察象ãšããŠããç°å¢ã«ããããŠæ€èšŒãšèª¿æŽãããå¿
èŠãããç¹ã«ã¯æ³šæãå¿
èŠã§ãã åäœæ€èšŒäžã«ãæ©çš®äŸåãšæãããæåãããäŸã玹ä»ããŸãã ãªãã·ã§ã³ æå frameRate æå®ãããšäžéš Android 端æ«ã§ä»¥äžã®æåãããã 1. Android 端æ«ã§é¢æ¥ã«åå ãã 2. iOS Safari ã§åå ãã 3. Android åŽã®æ åãšé³å£°ã Safari ã«éãããªã Safari ã§å
ã«åå ããå Žåã«ã¯åé¡ããªãã è§£å床æå® äŸ: width: 320, height: 320 frameRate ã ãæå®ããŠãããšãã«äžèšã®æåããã AndroidãiOS Safari ã®çµã¿åããã§åé¡ãèµ·ãããªããªã£ãã è§£å床ãæå®ãããšã€ã³ã«ã¡ã©ã§ã¯ãªãããªã¢ã«ã¡ã©ã䜿ã Android 端æ«ããã£ãã å¶çŽèšå®ã«ã€ããŠã¯ãçŸåšã調æŽäžã§ãã åèæ
å ± SkyWay Conference conf.webrtc.ecl.ntt.com github.com SkyWay ã®ã«ãŒã æ©èœã䜿çšãããã¢ç°å¢ã§ãã GitHub ã«ã³ãŒããå
¬éãããŠããã®ã§ãã«ãŒã ã䜿çšããå Žåã®æåãšå®è£
æ¹æ³ã確èªã§ããŸãã éçºãããŠãããšãå®è£
ã®åé¡ãªã®ããSkyWay ã® SDK ã®ä»æ§ãªã®ããç¹å®ã®ããã€ã¹ã§èµ·ããåé¡ãªã®ãã®åãåãã«æéããããããããã¡ãã®ç°å¢ãåèã«ãªããŸããã ãŸãšã SkyWay ã WebRTC ãšã°ã«ãŒãã§ã®ãããªé話ã®å®è£
ãçµ±åããŠæäŸããŠããããããéçºæã¯ãèªç€ŸãµãŒãã¹ãšããŠã® WEB 颿¥ã®æ©èœã«éäžããããšãã§ããŸããã ã¹ããŒããã©ã³ã®ãã©ãŠã¶å¯Ÿå¿ãšèª¿æ»ã«æéããããããšããããŸãããä»åŸãå©çšè
ããã®ãã£ãŒãããã¯ãåŸãªããæ¹åããŠãããããšæããŸãã ã¡ãã¬ãŒã§ã¯ãããŒãºã«ããããæ°æ©èœã®éçºã«ãåãå
¥ããŠããŸããå€ãã®å©çšè
ã«å®éã«äœ¿ããããµãŒãã¹ã®éçºãããŠã¿ãããšæã£ãæ¹ããã²ãæ°è»œã«ã話ããŸãããïŒ www.medley.jp
æ ªåŒäŒç€Ÿã¡ãã¬ãŒã®ãšã³ãžãã¢ã®ç¬¹å¡ã§ãã ç§ãéçºãæ
åœããŠãããžã§ãã¡ãã¬ãŒã§ãå
æ 10 æ 23 æ¥ã« WEB 颿¥ã»åç»éžèããªãªãŒã¹ããŸããã job-medley.com WEB 颿¥ãåç»éžèãšãã«ãæšä»ã®é察é¢ã§ã®å°±è·æŽ»åããŒãºã«å¿ããã¹ãéçºããŸããã ãªãªãŒã¹ã¯ 2 ã€ã®æ©èœãåæã§ãããä»å㯠WEB 颿¥ã®è£åŽã«çµã£ãŠã玹ä»ããŸãã WEB 颿¥æŠèŠ WEB 颿¥ãšã¯ããªã¢ã«ã¿ã€ã ã§äºæ¥è
æ§ãšæ±è·è
æ§ãããªã³ã©ã€ã³é¢æ¥ãè¡ãããšãã§ããæ©èœã§ãã å°çšã®ã¢ããªã±ãŒã·ã§ã³ã¯å¿
èŠãªããPCãã¹ããŒããã©ã³ã®ãã©ãŠã¶ããå©çšã§ããŸãã ãµãŒãã¹éžå® éçºã«ããããããã€ãã®åè£ãããããŸããããæçµçã«ã¯èªç€Ÿå
ã§ãå°å
¥å®çžŸã®ãã SkyWay ã䜿çšããããšã«ããŸããã SkyWay ãšã¯ WebRTCïŒWeb Real Time CommunicationïŒã䜿çšãããªã³ã©ã€ã³ã®ãããªé話ãããµãŒãã¹ã«å°å
¥ã§ãããã«ããã©ãããã©ãŒã SDK ã§ãã 2020 幎 11 ææç¹ã§ãJavaScript SDKãiOS SDKãAndroid SDK ãæäŸãããŠããŸãã ã·ã°ããªã³ã°ãµãŒããªã©ã® WebRTC ã«å¿
èŠãšãªãã€ã³ãã©æ§ç¯ãäžèŠã§ã 䜿çšäžéã€ãã®ç¡æãã©ã³ããããŸã NTT ã³ãã¥ãã±ãŒã·ã§ã³ãºãéçºããŠããŸã webrtc.ecl.ntt.com WEB 颿¥ã®å¯Ÿå¿ãã©ãŠã¶ããŒãžã§ã³ïŒ2020 幎 11 ææç¹ïŒ ãã©ãããã©ãŒã 察å¿ããŒãžã§ã³ PC: Google Chrome ããŒãžã§ã³ 84 ä»¥äž PC: Microsoft Edge ããŒãžã§ã³ 84 ä»¥äž iOS: Safari iOS12 ä»¥äž Android: Google Chrome ããŒãžã§ã³ 85 ä»¥äž Andoid9 ä»¥äž SkyWay JavaScript SDK ã®åäœç¢ºèªãã©ãŠã¶ãWebRTC ã®å¯Ÿå¿ç¶æ³ãå©çšè
ã®å©çšåŸåãã察å¿ãã©ãŠã¶ã®ããŒãžã§ã³ãäžèšã®ããã«èšå®ããŸããã webrtc.ecl.ntt.com SkyWay ã®æ¥ç¶ã¢ãã« SkyWay ã§ãããªé話ãå®è£
ããå Žåã2 çš®é¡ã®æ¥ç¶ã¢ãã«ããéžã³ãŸãã SkyWay é»è©±ã¢ãã« é»è©±ã®ããã« 1 察 1 ã§ã®ãããªéè©±ãæ³å®ããã¢ãã«ã§ãã Peer ã€ã³ã¹ã¿ã³ã¹(ã·ã°ããªã³ã°ãµãŒãã«ãã£ãŠçºè¡ãããäžæã® PeerID ãæã€)åå£«ã§æ¥ç¶ããŸãã æ¥ç¶ããããã«ã¯ãçžæã® PeerID ãå¿
èŠã«ãªããŸãã SkyWay ã«ãŒã ã¢ãã« åäžã«ãŒã å
ã®å
šãŠã® Peer ã§ãããªé話ããã¢ãã«ã§ãã ã«ãŒã åã䜿çšããŠåå ããŸããçžæã® PeerID ãç¥ãå¿
èŠã¯ãããŸããã ã«ãŒã å㯠API ããŒæ¯ã«ç¬ç«ããŠããŸãã ã«ãŒã ã®æ¥ç¶ã¿ã€ãã¯ãã«ã¡ãã·ã¥ã SFU ã® 2 çš®é¡ããéžã¹ãŸãã åå è
å
šå¡ãžã®ãã£ãããªã©ã®ããŒã¿éä¿¡ãã§ããŸãã ãžã§ãã¡ãã¬ãŒã® WEB 颿¥ã§ã¯ã ä»åŸã®æ©èœæ¡åŒµãæ³å®ããŠããã¡ãã®ã«ãŒã ã¢ãã«ãæ¡çšããŸããã ã«ãŒã ã®éä¿¡ã¿ã€ã ã«ãŒã ã«è€æ°äººãåå ããŠããå Žåãããããã®éä¿¡ãã©ãè¡ããããã¡ãã·ã¥ãš SFU ããéžã¶ããšãã§ããŸãã ãã«ã¡ãã·ã¥ ç»ååŒçšå
: https://webrtc.ecl.ntt.com/skyway/overview.html#_4-sfu%E3%82%B5%E3%83%BC%E3%83%90 å
šå¡ãçžäºã«éä¿¡ãè¡ããŸãã人æ°ãå¢ãããšã人æ°å端æ«ã®ãšã³ã³ãŒãè² è·ãšéä¿¡éãå¢å ããŸãã SFU SFU ã®å Žåãäžãã®æ¥ç¶ã¯ 1 æ¬ã«ãªãã®ã§ãã¡ãã·ã¥ããã端æ«ã®ãšã³ã³ãŒãè² è·ããéä¿¡éã®è»œæžãæåŸ
ã§ããŸãã ç»ååŒçšå
: https://webrtc.ecl.ntt.com/skyway/overview.html#_4-sfu%E3%82%B5%E3%83%BC%E3%83%90 éä¿¡æ¹åŒã®éã㯠SkyWay ãé èœããŠãããã®ã§ãjoinRoom æã® mode ã mesh ãã sfu ã«å€æŽããã ãã§åãæ¿ãããŸãã peer . joinRoom ( roomName , { mode: "mesh ã sfu ãæå®" , stream: mediaStream }); webrtc.ecl.ntt.com ãžã§ãã¡ãã¬ãŒã® WEB 颿¥ã§ã¯ã颿¥åå 人æ°ãèæ
®ã㊠mesh ã䜿çšããŠããŸãã å®è£
ã€ã¡ãŒãž const peer = new Peer ( peer_id , { key: api_key , }); peer . once ( "open" , () => { room . once ( "open" , () => { // ã«ãŒã åå åŸã«çºçããã€ãã³ã }); room . on ( "peerJoin" , ( peerId : string ) => { // ã«ãŒã ã«èª°ãåå ããå Žåã«çºçããã€ãã³ã }); room . on ( "stream" , ( stream : RoomStream ) => { // stream ãåããå Žåã«çºçããã€ãã³ã }); room . on ( "data" , ({ src , data }) => { // data ãåããå Žåã«çºçããã€ãã³ã }); }); peer . on ( "error" , ( error : Error ) => { // ãšã©ãŒçºçæã«çºçããã€ãã³ã }); peer . joinRoom ( roomName , { mode: "mesh" , stream: mediaStream }); room . close (); peer . disconnect (); Peer ãäœæã peer.joinRoom() ã§ã«ãŒã ã«åå room.stream ã€ãã³ãã§ä»ã®åå è
ã® stream ãåãåã room.data() ã§ãã£ãããªã©ãããŒã¿éä¿¡ãã§ãã room.close() ã§ã«ãŒã ããéåº ã SkyWay ã® JavaScript SDK ã䜿çšããåºæ¬çãªå®è£
ã«ãªããŸãã ãã®å®è£
ã« navigator.mediaDevices.getUserMedia() ã§ååŸãã stream ã joinRoom ã§æž¡ã steam ã€ãã³ãã§åãåã£ã stream ã video ã§åçãã ã远å ããã°ããªã³ã©ã€ã³ã§ã®ãããªé話ãå¯èœã«ãªããŸãã ã¹ããŒããã©ã³å¯Ÿå¿ PC ãšåæ§ã®ã³ãŒãã§ã»ãŒåäœããŸããããiOS ã Android 端æ«ã§ãæ©çš®äŸåãšæãããæåã®èª¿æ»ãšå¯Ÿå¿ã«æéãããããŸããããã®äžéšã玹ä»ããŸãã ã¿ããç§»åãããšæ åãæ ããªã çºçããåé¡ iOS12ãiOS13 ãªã©ã§ è€æ°ã®ã¿ããã²ãããå Žåã«ãæ åã®ååŸãã§ããªããªã ã¹ã¯ãªãŒã³ããã¯ããã®åŸ©åž°æã«ãæ åãååŸã§ããªããªã ãšããåé¡ãèµ·ããŸãããiOS14 ã§ã¯ã¿ãåãæ¿ãæã«æ åãååŸã§ããããã«ãªã£ãŠããŸããããã¹ã¯ãªãŒã³ããã¯ããã®åŸ©åž°æã¯æ åãååŸã§ããªããŸãŸã§ããã å¯Ÿå¿ ãã®å¯Ÿå¿ã¯ visibilitychange ã€ãã³ãã§ãã¿ãã®åãæ¿ããšãã¹ã¯ãªãŒã³ããã¯ããã®åŸ©åž°æã®ã€ãã³ããæŸã ååŸæžã® stream ã® track ã stop ãã stream ãåãçŽã SkyWay ã® room.replaceStream() ã§ãWebRTC ã§äœ¿çšããŠãã stream ãå·®ãæ¿ãã 以äžã®å®è£
ã«ããã察å¿ããŸããã __ document . addEventListener ( "visibilitychange" , async () => { if ( document . visibilityState !== "visible" ) { return ; } localMedia . getTracks (). forEach (( track : MediaStreamTrack ) => { track . stop (); }); const replaceStream = await navigator . mediaDevices . getUserMedia ({ video: true , audio: true , }); room . replaceStream ( replaceStream ); }); ã€ã€ãã³ã®æäœã§æ åãæ¢ãŸã çºçããåé¡ iOS12ãiOS13 ã§ã¯èµ·ãããŸããã§ããããiOS14 ã§ãªã³ã©ã€ã³é¢æ¥éäžã«ã€ã€ãã³ãã¹ããŒããã©ã³ããå€ããšãçžæåŽã®æ åãæ¢ãŸãããã«ãªããŸããã å¯Ÿå¿ ãããŸã§ã¯ãçžæã®æ åãšé³å£°ãåçããããã«ãvideo ã¿ã°ã« stream ãæž¡ããŠæ åãšé³å£°ãåçããŠããŸããã video : mute ã«ããŠæ åãåç audio : é³å£°ãåç ãšãé³å£°ãšåçãåãããšãããã€ã€ãã³ãå€ããŠã忢ããããšã¯ãªããªããŸããã å¶çŽèšå® getUserMedia ã§ååŸãã MediaStream ã¯ãå¶çŽãèšå®ããããšã§ããã€ã¹ã®æ¶è²»ãªãœãŒã¹ãæããããšãã§ããŸãã èšå®äŸ: navigator . mediaDevices . getUserMedia ({ audio: true , video: true }); â navigator . mediaDevices . getUserMedia ({ audio: true , video: { frameRate: 15 } }); å¶çŽãèšå®å¯èœãã©ããã®ç¢ºèª: getSupportedConstraints() ã§ã察å¿ããŠããå¶çŽåãååŸããããšãã§ããŸãã const supportedConstraints = navigator . mediaDevices . getSupportedConstraints (); 䜿çšããŠãããã©ãŠã¶ããã®å¶çŽã«å¯Ÿå¿ããŠãããã確èªãã察å¿ããŠããå Žåã®ã¿èšå®ãæå¹ã«ããŸãã äŸãã°ãã¹ããŒããã©ã³ã®å Žåã«ä»¥äžã®èšå®ãããã°ãã€ã³ã«ã¡ã©ã䜿çšãããã¬ãŒã ã¬ãŒãã 20 ã«æãã320x320 ã®è§£å床ã«å¶éããããšãã§ããŸãã const options = { facingMode: "user" , frameRate: 20 , width: 320 , height: 320 }; æå®ããå¶éãå¿
ã䜿çšãããä¿èšŒã¯ãªããæ©çš®äŸåã®åœ±é¿ãåããèšå®ã§ãããã®ã§ã察象ãšããŠããç°å¢ã«ããããŠæ€èšŒãšèª¿æŽãããå¿
èŠãããç¹ã«ã¯æ³šæãå¿
èŠã§ãã åäœæ€èšŒäžã«ãæ©çš®äŸåãšæãããæåãããäŸã玹ä»ããŸãã ãªãã·ã§ã³ æå frameRate æå®ãããšäžéš Android 端æ«ã§ä»¥äžã®æåãããã 1. Android 端æ«ã§é¢æ¥ã«åå ãã 2. iOS Safari ã§åå ãã 3. Android åŽã®æ åãšé³å£°ã Safari ã«éãããªã Safari ã§å
ã«åå ããå Žåã«ã¯åé¡ããªãã è§£å床æå® äŸ: width: 320, height: 320 frameRate ã ãæå®ããŠãããšãã«äžèšã®æåããã AndroidãiOS Safari ã®çµã¿åããã§åé¡ãèµ·ãããªããªã£ãã è§£å床ãæå®ãããšã€ã³ã«ã¡ã©ã§ã¯ãªãããªã¢ã«ã¡ã©ã䜿ã Android 端æ«ããã£ãã å¶çŽèšå®ã«ã€ããŠã¯ãçŸåšã調æŽäžã§ãã åèæ
å ± SkyWay Conference conf.webrtc.ecl.ntt.com github.com SkyWay ã®ã«ãŒã æ©èœã䜿çšãããã¢ç°å¢ã§ãã GitHub ã«ã³ãŒããå
¬éãããŠããã®ã§ãã«ãŒã ã䜿çšããå Žåã®æåãšå®è£
æ¹æ³ã確èªã§ããŸãã éçºãããŠãããšãå®è£
ã®åé¡ãªã®ããSkyWay ã® SDK ã®ä»æ§ãªã®ããç¹å®ã®ããã€ã¹ã§èµ·ããåé¡ãªã®ãã®åãåãã«æéããããããããã¡ãã®ç°å¢ãåèã«ãªããŸããã ãŸãšã SkyWay ã WebRTC ãšã°ã«ãŒãã§ã®ãããªé話ã®å®è£
ãçµ±åããŠæäŸããŠããããããéçºæã¯ãèªç€ŸãµãŒãã¹ãšããŠã® WEB 颿¥ã®æ©èœã«éäžããããšãã§ããŸããã ã¹ããŒããã©ã³ã®ãã©ãŠã¶å¯Ÿå¿ãšèª¿æ»ã«æéããããããšããããŸãããä»åŸãå©çšè
ããã®ãã£ãŒãããã¯ãåŸãªããæ¹åããŠãããããšæããŸãã ã¡ãã¬ãŒã§ã¯ãããŒãºã«ããããæ°æ©èœã®éçºã«ãåãå
¥ããŠããŸããå€ãã®å©çšè
ã«å®éã«äœ¿ããããµãŒãã¹ã®éçºãããŠã¿ãããšæã£ãæ¹ããã²ãæ°è»œã«ã話ããŸãããïŒ www.medley.jp
æ ªåŒäŒç€Ÿã¡ãã¬ãŒã®ãšã³ãžãã¢ã®ç¬¹å¡ã§ãã ç§ãéçºãæ
åœããŠãããžã§ãã¡ãã¬ãŒã§ãå
æ 10 æ 23 æ¥ã« WEB 颿¥ã»åç»éžèããªãªãŒã¹ããŸããã job-medley.com WEB 颿¥ãåç»éžèãšãã«ãæšä»ã®é察é¢ã§ã®å°±è·æŽ»åããŒãºã«å¿ããã¹ãéçºããŸããã ãªãªãŒã¹ã¯ 2 ã€ã®æ©èœãåæã§ãããä»å㯠WEB 颿¥ã®è£åŽã«çµã£ãŠã玹ä»ããŸãã WEB 颿¥æŠèŠ WEB 颿¥ãšã¯ããªã¢ã«ã¿ã€ã ã§äºæ¥è
æ§ãšæ±è·è
æ§ãããªã³ã©ã€ã³é¢æ¥ãè¡ãããšãã§ããæ©èœã§ãã å°çšã®ã¢ããªã±ãŒã·ã§ã³ã¯å¿
èŠãªããPCãã¹ããŒããã©ã³ã®ãã©ãŠã¶ããå©çšã§ããŸãã ãµãŒãã¹éžå® éçºã«ããããããã€ãã®åè£ãããããŸããããæçµçã«ã¯èªç€Ÿå
ã§ãå°å
¥å®çžŸã®ãã SkyWay ã䜿çšããããšã«ããŸããã SkyWay ãšã¯ WebRTCïŒWeb Real Time CommunicationïŒã䜿çšãããªã³ã©ã€ã³ã®ãããªé話ãããµãŒãã¹ã«å°å
¥ã§ãããã«ããã©ãããã©ãŒã SDK ã§ãã 2020 幎 11 ææç¹ã§ãJavaScript SDKãiOS SDKãAndroid SDK ãæäŸãããŠããŸãã ã·ã°ããªã³ã°ãµãŒããªã©ã® WebRTC ã«å¿
èŠãšãªãã€ã³ãã©æ§ç¯ãäžèŠã§ã 䜿çšäžéã€ãã®ç¡æãã©ã³ããããŸã NTT ã³ãã¥ãã±ãŒã·ã§ã³ãºãéçºããŠããŸã webrtc.ecl.ntt.com WEB 颿¥ã®å¯Ÿå¿ãã©ãŠã¶ããŒãžã§ã³ïŒ2020 幎 11 ææç¹ïŒ ãã©ãããã©ãŒã 察å¿ããŒãžã§ã³ PC: Google Chrome ããŒãžã§ã³ 84 ä»¥äž PC: Microsoft Edge ããŒãžã§ã³ 84 ä»¥äž iOS: Safari iOS12 ä»¥äž Android: Google Chrome ããŒãžã§ã³ 85 ä»¥äž Andoid9 ä»¥äž SkyWay JavaScript SDK ã®åäœç¢ºèªãã©ãŠã¶ãWebRTC ã®å¯Ÿå¿ç¶æ³ãå©çšè
ã®å©çšåŸåãã察å¿ãã©ãŠã¶ã®ããŒãžã§ã³ãäžèšã®ããã«èšå®ããŸããã webrtc.ecl.ntt.com SkyWay ã®æ¥ç¶ã¢ãã« SkyWay ã§ãããªé話ãå®è£
ããå Žåã2 çš®é¡ã®æ¥ç¶ã¢ãã«ããéžã³ãŸãã SkyWay é»è©±ã¢ãã« é»è©±ã®ããã« 1 察 1 ã§ã®ãããªéè©±ãæ³å®ããã¢ãã«ã§ãã Peer ã€ã³ã¹ã¿ã³ã¹(ã·ã°ããªã³ã°ãµãŒãã«ãã£ãŠçºè¡ãããäžæã® PeerID ãæã€)åå£«ã§æ¥ç¶ããŸãã æ¥ç¶ããããã«ã¯ãçžæã® PeerID ãå¿
èŠã«ãªããŸãã SkyWay ã«ãŒã ã¢ãã« åäžã«ãŒã å
ã®å
šãŠã® Peer ã§ãããªé話ããã¢ãã«ã§ãã ã«ãŒã åã䜿çšããŠåå ããŸããçžæã® PeerID ãç¥ãå¿
èŠã¯ãããŸããã ã«ãŒã å㯠API ããŒæ¯ã«ç¬ç«ããŠããŸãã ã«ãŒã ã®æ¥ç¶ã¿ã€ãã¯ãã«ã¡ãã·ã¥ã SFU ã® 2 çš®é¡ããéžã¹ãŸãã åå è
å
šå¡ãžã®ãã£ãããªã©ã®ããŒã¿éä¿¡ãã§ããŸãã ãžã§ãã¡ãã¬ãŒã® WEB 颿¥ã§ã¯ã ä»åŸã®æ©èœæ¡åŒµãæ³å®ããŠããã¡ãã®ã«ãŒã ã¢ãã«ãæ¡çšããŸããã ã«ãŒã ã®éä¿¡ã¿ã€ã ã«ãŒã ã«è€æ°äººãåå ããŠããå Žåãããããã®éä¿¡ãã©ãè¡ããããã¡ãã·ã¥ãš SFU ããéžã¶ããšãã§ããŸãã ãã«ã¡ãã·ã¥ ç»ååŒçšå
: https://webrtc.ecl.ntt.com/skyway/overview.html#_4-sfu%E3%82%B5%E3%83%BC%E3%83%90 å
šå¡ãçžäºã«éä¿¡ãè¡ããŸãã人æ°ãå¢ãããšã人æ°å端æ«ã®ãšã³ã³ãŒãè² è·ãšéä¿¡éãå¢å ããŸãã SFU SFU ã®å Žåãäžãã®æ¥ç¶ã¯ 1 æ¬ã«ãªãã®ã§ãã¡ãã·ã¥ããã端æ«ã®ãšã³ã³ãŒãè² è·ããéä¿¡éã®è»œæžãæåŸ
ã§ããŸãã ç»ååŒçšå
: https://webrtc.ecl.ntt.com/skyway/overview.html#_4-sfu%E3%82%B5%E3%83%BC%E3%83%90 éä¿¡æ¹åŒã®éã㯠SkyWay ãé èœããŠãããã®ã§ãjoinRoom æã® mode ã mesh ãã sfu ã«å€æŽããã ãã§åãæ¿ãããŸãã peer . joinRoom ( roomName , { mode: "mesh ã sfu ãæå®" , stream: mediaStream }); webrtc.ecl.ntt.com ãžã§ãã¡ãã¬ãŒã® WEB 颿¥ã§ã¯ã颿¥åå 人æ°ãèæ
®ã㊠mesh ã䜿çšããŠããŸãã å®è£
ã€ã¡ãŒãž const peer = new Peer ( peer_id , { key: api_key , }); peer . once ( "open" , () => { room . once ( "open" , () => { // ã«ãŒã åå åŸã«çºçããã€ãã³ã }); room . on ( "peerJoin" , ( peerId : string ) => { // ã«ãŒã ã«èª°ãåå ããå Žåã«çºçããã€ãã³ã }); room . on ( "stream" , ( stream : RoomStream ) => { // stream ãåããå Žåã«çºçããã€ãã³ã }); room . on ( "data" , ({ src , data }) => { // data ãåããå Žåã«çºçããã€ãã³ã }); }); peer . on ( "error" , ( error : Error ) => { // ãšã©ãŒçºçæã«çºçããã€ãã³ã }); peer . joinRoom ( roomName , { mode: "mesh" , stream: mediaStream }); room . close (); peer . disconnect (); Peer ãäœæã peer.joinRoom() ã§ã«ãŒã ã«åå room.stream ã€ãã³ãã§ä»ã®åå è
ã® stream ãåãåã room.data() ã§ãã£ãããªã©ãããŒã¿éä¿¡ãã§ãã room.close() ã§ã«ãŒã ããéåº ã SkyWay ã® JavaScript SDK ã䜿çšããåºæ¬çãªå®è£
ã«ãªããŸãã ãã®å®è£
ã« navigator.mediaDevices.getUserMedia() ã§ååŸãã stream ã joinRoom ã§æž¡ã steam ã€ãã³ãã§åãåã£ã stream ã video ã§åçãã ã远å ããã°ããªã³ã©ã€ã³ã§ã®ãããªé話ãå¯èœã«ãªããŸãã ã¹ããŒããã©ã³å¯Ÿå¿ PC ãšåæ§ã®ã³ãŒãã§ã»ãŒåäœããŸããããiOS ã Android 端æ«ã§ãæ©çš®äŸåãšæãããæåã®èª¿æ»ãšå¯Ÿå¿ã«æéãããããŸããããã®äžéšã玹ä»ããŸãã ã¿ããç§»åãããšæ åãæ ããªã çºçããåé¡ iOS12ãiOS13 ãªã©ã§ è€æ°ã®ã¿ããã²ãããå Žåã«ãæ åã®ååŸãã§ããªããªã ã¹ã¯ãªãŒã³ããã¯ããã®åŸ©åž°æã«ãæ åãååŸã§ããªããªã ãšããåé¡ãèµ·ããŸãããiOS14 ã§ã¯ã¿ãåãæ¿ãæã«æ åãååŸã§ããããã«ãªã£ãŠããŸããããã¹ã¯ãªãŒã³ããã¯ããã®åŸ©åž°æã¯æ åãååŸã§ããªããŸãŸã§ããã å¯Ÿå¿ ãã®å¯Ÿå¿ã¯ visibilitychange ã€ãã³ãã§ãã¿ãã®åãæ¿ããšãã¹ã¯ãªãŒã³ããã¯ããã®åŸ©åž°æã®ã€ãã³ããæŸã ååŸæžã® stream ã® track ã stop ãã stream ãåãçŽã SkyWay ã® room.replaceStream() ã§ãWebRTC ã§äœ¿çšããŠãã stream ãå·®ãæ¿ãã 以äžã®å®è£
ã«ããã察å¿ããŸããã __ document . addEventListener ( "visibilitychange" , async () => { if ( document . visibilityState !== "visible" ) { return ; } localMedia . getTracks (). forEach (( track : MediaStreamTrack ) => { track . stop (); }); const replaceStream = await navigator . mediaDevices . getUserMedia ({ video: true , audio: true , }); room . replaceStream ( replaceStream ); }); ã€ã€ãã³ã®æäœã§æ åãæ¢ãŸã çºçããåé¡ iOS12ãiOS13 ã§ã¯èµ·ãããŸããã§ããããiOS14 ã§ãªã³ã©ã€ã³é¢æ¥éäžã«ã€ã€ãã³ãã¹ããŒããã©ã³ããå€ããšãçžæåŽã®æ åãæ¢ãŸãããã«ãªããŸããã å¯Ÿå¿ ãããŸã§ã¯ãçžæã®æ åãšé³å£°ãåçããããã«ãvideo ã¿ã°ã« stream ãæž¡ããŠæ åãšé³å£°ãåçããŠããŸããã video : mute ã«ããŠæ åãåç audio : é³å£°ãåç ãšãé³å£°ãšåçãåãããšãããã€ã€ãã³ãå€ããŠã忢ããããšã¯ãªããªããŸããã å¶çŽèšå® getUserMedia ã§ååŸãã MediaStream ã¯ãå¶çŽãèšå®ããããšã§ããã€ã¹ã®æ¶è²»ãªãœãŒã¹ãæããããšãã§ããŸãã èšå®äŸ: navigator . mediaDevices . getUserMedia ({ audio: true , video: true }); â navigator . mediaDevices . getUserMedia ({ audio: true , video: { frameRate: 15 } }); å¶çŽãèšå®å¯èœãã©ããã®ç¢ºèª: getSupportedConstraints() ã§ã察å¿ããŠããå¶çŽåãååŸããããšãã§ããŸãã const supportedConstraints = navigator . mediaDevices . getSupportedConstraints (); 䜿çšããŠãããã©ãŠã¶ããã®å¶çŽã«å¯Ÿå¿ããŠãããã確èªãã察å¿ããŠããå Žåã®ã¿èšå®ãæå¹ã«ããŸãã äŸãã°ãã¹ããŒããã©ã³ã®å Žåã«ä»¥äžã®èšå®ãããã°ãã€ã³ã«ã¡ã©ã䜿çšãããã¬ãŒã ã¬ãŒãã 20 ã«æãã320x320 ã®è§£å床ã«å¶éããããšãã§ããŸãã const options = { facingMode: "user" , frameRate: 20 , width: 320 , height: 320 }; æå®ããå¶éãå¿
ã䜿çšãããä¿èšŒã¯ãªããæ©çš®äŸåã®åœ±é¿ãåããèšå®ã§ãããã®ã§ã察象ãšããŠããç°å¢ã«ããããŠæ€èšŒãšèª¿æŽãããå¿
èŠãããç¹ã«ã¯æ³šæãå¿
èŠã§ãã åäœæ€èšŒäžã«ãæ©çš®äŸåãšæãããæåãããäŸã玹ä»ããŸãã ãªãã·ã§ã³ æå frameRate æå®ãããšäžéš Android 端æ«ã§ä»¥äžã®æåãããã 1. Android 端æ«ã§é¢æ¥ã«åå ãã 2. iOS Safari ã§åå ãã 3. Android åŽã®æ åãšé³å£°ã Safari ã«éãããªã Safari ã§å
ã«åå ããå Žåã«ã¯åé¡ããªãã è§£å床æå® äŸ: width: 320, height: 320 frameRate ã ãæå®ããŠãããšãã«äžèšã®æåããã AndroidãiOS Safari ã®çµã¿åããã§åé¡ãèµ·ãããªããªã£ãã è§£å床ãæå®ãããšã€ã³ã«ã¡ã©ã§ã¯ãªãããªã¢ã«ã¡ã©ã䜿ã Android 端æ«ããã£ãã å¶çŽèšå®ã«ã€ããŠã¯ãçŸåšã調æŽäžã§ãã åèæ
å ± SkyWay Conference conf.webrtc.ecl.ntt.com github.com SkyWay ã®ã«ãŒã æ©èœã䜿çšãããã¢ç°å¢ã§ãã GitHub ã«ã³ãŒããå
¬éãããŠããã®ã§ãã«ãŒã ã䜿çšããå Žåã®æåãšå®è£
æ¹æ³ã確èªã§ããŸãã éçºãããŠãããšãå®è£
ã®åé¡ãªã®ããSkyWay ã® SDK ã®ä»æ§ãªã®ããç¹å®ã®ããã€ã¹ã§èµ·ããåé¡ãªã®ãã®åãåãã«æéããããããããã¡ãã®ç°å¢ãåèã«ãªããŸããã ãŸãšã SkyWay ã WebRTC ãšã°ã«ãŒãã§ã®ãããªé話ã®å®è£
ãçµ±åããŠæäŸããŠããããããéçºæã¯ãèªç€ŸãµãŒãã¹ãšããŠã® WEB 颿¥ã®æ©èœã«éäžããããšãã§ããŸããã ã¹ããŒããã©ã³ã®ãã©ãŠã¶å¯Ÿå¿ãšèª¿æ»ã«æéããããããšããããŸãããä»åŸãå©çšè
ããã®ãã£ãŒãããã¯ãåŸãªããæ¹åããŠãããããšæããŸãã ã¡ãã¬ãŒã§ã¯ãããŒãºã«ããããæ°æ©èœã®éçºã«ãåãå
¥ããŠããŸããå€ãã®å©çšè
ã«å®éã«äœ¿ããããµãŒãã¹ã®éçºãããŠã¿ãããšæã£ãæ¹ããã²ãæ°è»œã«ã話ããŸãããïŒ www.medley.jp
æ ªåŒäŒç€Ÿã¡ãã¬ãŒã®ãšã³ãžãã¢ã®ç¬¹å¡ã§ãã ç§ãéçºãæ
åœããŠãããžã§ãã¡ãã¬ãŒã§ãå
æ 10 æ 23 æ¥ã« WEB 颿¥ã»åç»éžèããªãªãŒã¹ããŸããã job-medley.com WEB 颿¥ãåç»éžèãšãã«ãæšä»ã®é察é¢ã§ã®å°±è·æŽ»åããŒãºã«å¿ããã¹ãéçºããŸããã ãªãªãŒã¹ã¯ 2 ã€ã®æ©èœãåæã§ãããä»å㯠WEB 颿¥ã®è£åŽã«çµã£ãŠã玹ä»ããŸãã WEB 颿¥æŠèŠ WEB 颿¥ãšã¯ããªã¢ã«ã¿ã€ã ã§äºæ¥è
æ§ãšæ±è·è
æ§ãããªã³ã©ã€ã³é¢æ¥ãè¡ãããšãã§ããæ©èœã§ãã å°çšã®ã¢ããªã±ãŒã·ã§ã³ã¯å¿
èŠãªããPCãã¹ããŒããã©ã³ã®ãã©ãŠã¶ããå©çšã§ããŸãã ãµãŒãã¹éžå® éçºã«ããããããã€ãã®åè£ãããããŸããããæçµçã«ã¯èªç€Ÿå
ã§ãå°å
¥å®çžŸã®ãã SkyWay ã䜿çšããããšã«ããŸããã SkyWay ãšã¯ WebRTCïŒWeb Real Time CommunicationïŒã䜿çšãããªã³ã©ã€ã³ã®ãããªé話ãããµãŒãã¹ã«å°å
¥ã§ãããã«ããã©ãããã©ãŒã SDK ã§ãã 2020 幎 11 ææç¹ã§ãJavaScript SDKãiOS SDKãAndroid SDK ãæäŸãããŠããŸãã ã·ã°ããªã³ã°ãµãŒããªã©ã® WebRTC ã«å¿
èŠãšãªãã€ã³ãã©æ§ç¯ãäžèŠã§ã 䜿çšäžéã€ãã®ç¡æãã©ã³ããããŸã NTT ã³ãã¥ãã±ãŒã·ã§ã³ãºãéçºããŠããŸã webrtc.ecl.ntt.com WEB 颿¥ã®å¯Ÿå¿ãã©ãŠã¶ããŒãžã§ã³ïŒ2020 幎 11 ææç¹ïŒ ãã©ãããã©ãŒã 察å¿ããŒãžã§ã³ PC: Google Chrome ããŒãžã§ã³ 84 ä»¥äž PC: Microsoft Edge ããŒãžã§ã³ 84 ä»¥äž iOS: Safari iOS12 ä»¥äž Android: Google Chrome ããŒãžã§ã³ 85 ä»¥äž Andoid9 ä»¥äž SkyWay JavaScript SDK ã®åäœç¢ºèªãã©ãŠã¶ãWebRTC ã®å¯Ÿå¿ç¶æ³ãå©çšè
ã®å©çšåŸåãã察å¿ãã©ãŠã¶ã®ããŒãžã§ã³ãäžèšã®ããã«èšå®ããŸããã webrtc.ecl.ntt.com SkyWay ã®æ¥ç¶ã¢ãã« SkyWay ã§ãããªé話ãå®è£
ããå Žåã2 çš®é¡ã®æ¥ç¶ã¢ãã«ããéžã³ãŸãã SkyWay é»è©±ã¢ãã« é»è©±ã®ããã« 1 察 1 ã§ã®ãããªéè©±ãæ³å®ããã¢ãã«ã§ãã Peer ã€ã³ã¹ã¿ã³ã¹(ã·ã°ããªã³ã°ãµãŒãã«ãã£ãŠçºè¡ãããäžæã® PeerID ãæã€)åå£«ã§æ¥ç¶ããŸãã æ¥ç¶ããããã«ã¯ãçžæã® PeerID ãå¿
èŠã«ãªããŸãã SkyWay ã«ãŒã ã¢ãã« åäžã«ãŒã å
ã®å
šãŠã® Peer ã§ãããªé話ããã¢ãã«ã§ãã ã«ãŒã åã䜿çšããŠåå ããŸããçžæã® PeerID ãç¥ãå¿
èŠã¯ãããŸããã ã«ãŒã å㯠API ããŒæ¯ã«ç¬ç«ããŠããŸãã ã«ãŒã ã®æ¥ç¶ã¿ã€ãã¯ãã«ã¡ãã·ã¥ã SFU ã® 2 çš®é¡ããéžã¹ãŸãã åå è
å
šå¡ãžã®ãã£ãããªã©ã®ããŒã¿éä¿¡ãã§ããŸãã ãžã§ãã¡ãã¬ãŒã® WEB 颿¥ã§ã¯ã ä»åŸã®æ©èœæ¡åŒµãæ³å®ããŠããã¡ãã®ã«ãŒã ã¢ãã«ãæ¡çšããŸããã ã«ãŒã ã®éä¿¡ã¿ã€ã ã«ãŒã ã«è€æ°äººãåå ããŠããå Žåãããããã®éä¿¡ãã©ãè¡ããããã¡ãã·ã¥ãš SFU ããéžã¶ããšãã§ããŸãã ãã«ã¡ãã·ã¥ ç»ååŒçšå
: https://webrtc.ecl.ntt.com/skyway/overview.html#_4-sfu%E3%82%B5%E3%83%BC%E3%83%90 å
šå¡ãçžäºã«éä¿¡ãè¡ããŸãã人æ°ãå¢ãããšã人æ°å端æ«ã®ãšã³ã³ãŒãè² è·ãšéä¿¡éãå¢å ããŸãã SFU SFU ã®å Žåãäžãã®æ¥ç¶ã¯ 1 æ¬ã«ãªãã®ã§ãã¡ãã·ã¥ããã端æ«ã®ãšã³ã³ãŒãè² è·ããéä¿¡éã®è»œæžãæåŸ
ã§ããŸãã ç»ååŒçšå
: https://webrtc.ecl.ntt.com/skyway/overview.html#_4-sfu%E3%82%B5%E3%83%BC%E3%83%90 éä¿¡æ¹åŒã®éã㯠SkyWay ãé èœããŠãããã®ã§ãjoinRoom æã® mode ã mesh ãã sfu ã«å€æŽããã ãã§åãæ¿ãããŸãã peer . joinRoom ( roomName , { mode: "mesh ã sfu ãæå®" , stream: mediaStream }); webrtc.ecl.ntt.com ãžã§ãã¡ãã¬ãŒã® WEB 颿¥ã§ã¯ã颿¥åå 人æ°ãèæ
®ã㊠mesh ã䜿çšããŠããŸãã å®è£
ã€ã¡ãŒãž const peer = new Peer ( peer_id , { key: api_key , }); peer . once ( "open" , () => { room . once ( "open" , () => { // ã«ãŒã åå åŸã«çºçããã€ãã³ã }); room . on ( "peerJoin" , ( peerId : string ) => { // ã«ãŒã ã«èª°ãåå ããå Žåã«çºçããã€ãã³ã }); room . on ( "stream" , ( stream : RoomStream ) => { // stream ãåããå Žåã«çºçããã€ãã³ã }); room . on ( "data" , ({ src , data }) => { // data ãåããå Žåã«çºçããã€ãã³ã }); }); peer . on ( "error" , ( error : Error ) => { // ãšã©ãŒçºçæã«çºçããã€ãã³ã }); peer . joinRoom ( roomName , { mode: "mesh" , stream: mediaStream }); room . close (); peer . disconnect (); Peer ãäœæã peer.joinRoom() ã§ã«ãŒã ã«åå room.stream ã€ãã³ãã§ä»ã®åå è
ã® stream ãåãåã room.data() ã§ãã£ãããªã©ãããŒã¿éä¿¡ãã§ãã room.close() ã§ã«ãŒã ããéåº ã SkyWay ã® JavaScript SDK ã䜿çšããåºæ¬çãªå®è£
ã«ãªããŸãã ãã®å®è£
ã« navigator.mediaDevices.getUserMedia() ã§ååŸãã stream ã joinRoom ã§æž¡ã steam ã€ãã³ãã§åãåã£ã stream ã video ã§åçãã ã远å ããã°ããªã³ã©ã€ã³ã§ã®ãããªé話ãå¯èœã«ãªããŸãã ã¹ããŒããã©ã³å¯Ÿå¿ PC ãšåæ§ã®ã³ãŒãã§ã»ãŒåäœããŸããããiOS ã Android 端æ«ã§ãæ©çš®äŸåãšæãããæåã®èª¿æ»ãšå¯Ÿå¿ã«æéãããããŸããããã®äžéšã玹ä»ããŸãã ã¿ããç§»åãããšæ åãæ ããªã çºçããåé¡ iOS12ãiOS13 ãªã©ã§ è€æ°ã®ã¿ããã²ãããå Žåã«ãæ åã®ååŸãã§ããªããªã ã¹ã¯ãªãŒã³ããã¯ããã®åŸ©åž°æã«ãæ åãååŸã§ããªããªã ãšããåé¡ãèµ·ããŸãããiOS14 ã§ã¯ã¿ãåãæ¿ãæã«æ åãååŸã§ããããã«ãªã£ãŠããŸããããã¹ã¯ãªãŒã³ããã¯ããã®åŸ©åž°æã¯æ åãååŸã§ããªããŸãŸã§ããã å¯Ÿå¿ ãã®å¯Ÿå¿ã¯ visibilitychange ã€ãã³ãã§ãã¿ãã®åãæ¿ããšãã¹ã¯ãªãŒã³ããã¯ããã®åŸ©åž°æã®ã€ãã³ããæŸã ååŸæžã® stream ã® track ã stop ãã stream ãåãçŽã SkyWay ã® room.replaceStream() ã§ãWebRTC ã§äœ¿çšããŠãã stream ãå·®ãæ¿ãã 以äžã®å®è£
ã«ããã察å¿ããŸããã __ document . addEventListener ( "visibilitychange" , async () => { if ( document . visibilityState !== "visible" ) { return ; } localMedia . getTracks (). forEach (( track : MediaStreamTrack ) => { track . stop (); }); const replaceStream = await navigator . mediaDevices . getUserMedia ({ video: true , audio: true , }); room . replaceStream ( replaceStream ); }); ã€ã€ãã³ã®æäœã§æ åãæ¢ãŸã çºçããåé¡ iOS12ãiOS13 ã§ã¯èµ·ãããŸããã§ããããiOS14 ã§ãªã³ã©ã€ã³é¢æ¥éäžã«ã€ã€ãã³ãã¹ããŒããã©ã³ããå€ããšãçžæåŽã®æ åãæ¢ãŸãããã«ãªããŸããã å¯Ÿå¿ ãããŸã§ã¯ãçžæã®æ åãšé³å£°ãåçããããã«ãvideo ã¿ã°ã« stream ãæž¡ããŠæ åãšé³å£°ãåçããŠããŸããã video : mute ã«ããŠæ åãåç audio : é³å£°ãåç ãšãé³å£°ãšåçãåãããšãããã€ã€ãã³ãå€ããŠã忢ããããšã¯ãªããªããŸããã å¶çŽèšå® getUserMedia ã§ååŸãã MediaStream ã¯ãå¶çŽãèšå®ããããšã§ããã€ã¹ã®æ¶è²»ãªãœãŒã¹ãæããããšãã§ããŸãã èšå®äŸ: navigator . mediaDevices . getUserMedia ({ audio: true , video: true }); â navigator . mediaDevices . getUserMedia ({ audio: true , video: { frameRate: 15 } }); å¶çŽãèšå®å¯èœãã©ããã®ç¢ºèª: getSupportedConstraints() ã§ã察å¿ããŠããå¶çŽåãååŸããããšãã§ããŸãã const supportedConstraints = navigator . mediaDevices . getSupportedConstraints (); 䜿çšããŠãããã©ãŠã¶ããã®å¶çŽã«å¯Ÿå¿ããŠãããã確èªãã察å¿ããŠããå Žåã®ã¿èšå®ãæå¹ã«ããŸãã äŸãã°ãã¹ããŒããã©ã³ã®å Žåã«ä»¥äžã®èšå®ãããã°ãã€ã³ã«ã¡ã©ã䜿çšãããã¬ãŒã ã¬ãŒãã 20 ã«æãã320x320 ã®è§£å床ã«å¶éããããšãã§ããŸãã const options = { facingMode: "user" , frameRate: 20 , width: 320 , height: 320 }; æå®ããå¶éãå¿
ã䜿çšãããä¿èšŒã¯ãªããæ©çš®äŸåã®åœ±é¿ãåããèšå®ã§ãããã®ã§ã察象ãšããŠããç°å¢ã«ããããŠæ€èšŒãšèª¿æŽãããå¿
èŠãããç¹ã«ã¯æ³šæãå¿
èŠã§ãã åäœæ€èšŒäžã«ãæ©çš®äŸåãšæãããæåãããäŸã玹ä»ããŸãã ãªãã·ã§ã³ æå frameRate æå®ãããšäžéš Android 端æ«ã§ä»¥äžã®æåãããã 1. Android 端æ«ã§é¢æ¥ã«åå ãã 2. iOS Safari ã§åå ãã 3. Android åŽã®æ åãšé³å£°ã Safari ã«éãããªã Safari ã§å
ã«åå ããå Žåã«ã¯åé¡ããªãã è§£å床æå® äŸ: width: 320, height: 320 frameRate ã ãæå®ããŠãããšãã«äžèšã®æåããã AndroidãiOS Safari ã®çµã¿åããã§åé¡ãèµ·ãããªããªã£ãã è§£å床ãæå®ãããšã€ã³ã«ã¡ã©ã§ã¯ãªãããªã¢ã«ã¡ã©ã䜿ã Android 端æ«ããã£ãã å¶çŽèšå®ã«ã€ããŠã¯ãçŸåšã調æŽäžã§ãã åèæ
å ± SkyWay Conference conf.webrtc.ecl.ntt.com github.com SkyWay ã®ã«ãŒã æ©èœã䜿çšãããã¢ç°å¢ã§ãã GitHub ã«ã³ãŒããå
¬éãããŠããã®ã§ãã«ãŒã ã䜿çšããå Žåã®æåãšå®è£
æ¹æ³ã確èªã§ããŸãã éçºãããŠãããšãå®è£
ã®åé¡ãªã®ããSkyWay ã® SDK ã®ä»æ§ãªã®ããç¹å®ã®ããã€ã¹ã§èµ·ããåé¡ãªã®ãã®åãåãã«æéããããããããã¡ãã®ç°å¢ãåèã«ãªããŸããã ãŸãšã SkyWay ã WebRTC ãšã°ã«ãŒãã§ã®ãããªé話ã®å®è£
ãçµ±åããŠæäŸããŠããããããéçºæã¯ãèªç€ŸãµãŒãã¹ãšããŠã® WEB 颿¥ã®æ©èœã«éäžããããšãã§ããŸããã ã¹ããŒããã©ã³ã®ãã©ãŠã¶å¯Ÿå¿ãšèª¿æ»ã«æéããããããšããããŸãããä»åŸãå©çšè
ããã®ãã£ãŒãããã¯ãåŸãªããæ¹åããŠãããããšæããŸãã ã¡ãã¬ãŒã§ã¯ãããŒãºã«ããããæ°æ©èœã®éçºã«ãåãå
¥ããŠããŸããå€ãã®å©çšè
ã«å®éã«äœ¿ããããµãŒãã¹ã®éçºãããŠã¿ãããšæã£ãæ¹ããã²ãæ°è»œã«ã話ããŸãããïŒ www.medley.jp
æ ªåŒäŒç€Ÿã¡ãã¬ãŒã®ãšã³ãžãã¢ã®ç¬¹å¡ã§ãã ç§ãéçºãæ
åœããŠãããžã§ãã¡ãã¬ãŒã§ãå
æ 10 æ 23 æ¥ã« WEB 颿¥ã»åç»éžèããªãªãŒã¹ããŸããã job-medley.com WEB 颿¥ãåç»éžèãšãã«ãæšä»ã®é察é¢ã§ã®å°±è·æŽ»åããŒãºã«å¿ããã¹ãéçºããŸããã ãªãªãŒã¹ã¯ 2 ã€ã®æ©èœãåæã§ãããä»å㯠WEB 颿¥ã®è£åŽã«çµã£ãŠã玹ä»ããŸãã WEB 颿¥æŠèŠ WEB 颿¥ãšã¯ããªã¢ã«ã¿ã€ã ã§äºæ¥è
æ§ãšæ±è·è
æ§ãããªã³ã©ã€ã³é¢æ¥ãè¡ãããšãã§ããæ©èœã§ãã å°çšã®ã¢ããªã±ãŒã·ã§ã³ã¯å¿
èŠãªããPCãã¹ããŒããã©ã³ã®ãã©ãŠã¶ããå©çšã§ããŸãã ãµãŒãã¹éžå® éçºã«ããããããã€ãã®åè£ãããããŸããããæçµçã«ã¯èªç€Ÿå
ã§ãå°å
¥å®çžŸã®ãã SkyWay ã䜿çšããããšã«ããŸããã SkyWay ãšã¯ WebRTCïŒWeb Real Time CommunicationïŒã䜿çšãããªã³ã©ã€ã³ã®ãããªé話ãããµãŒãã¹ã«å°å
¥ã§ãããã«ããã©ãããã©ãŒã SDK ã§ãã 2020 幎 11 ææç¹ã§ãJavaScript SDKãiOS SDKãAndroid SDK ãæäŸãããŠããŸãã ã·ã°ããªã³ã°ãµãŒããªã©ã® WebRTC ã«å¿
èŠãšãªãã€ã³ãã©æ§ç¯ãäžèŠã§ã 䜿çšäžéã€ãã®ç¡æãã©ã³ããããŸã NTT ã³ãã¥ãã±ãŒã·ã§ã³ãºãéçºããŠããŸã webrtc.ecl.ntt.com WEB 颿¥ã®å¯Ÿå¿ãã©ãŠã¶ããŒãžã§ã³ïŒ2020 幎 11 ææç¹ïŒ ãã©ãããã©ãŒã 察å¿ããŒãžã§ã³ PC: Google Chrome ããŒãžã§ã³ 84 ä»¥äž PC: Microsoft Edge ããŒãžã§ã³ 84 ä»¥äž iOS: Safari iOS12 ä»¥äž Android: Google Chrome ããŒãžã§ã³ 85 ä»¥äž Andoid9 ä»¥äž SkyWay JavaScript SDK ã®åäœç¢ºèªãã©ãŠã¶ãWebRTC ã®å¯Ÿå¿ç¶æ³ãå©çšè
ã®å©çšåŸåãã察å¿ãã©ãŠã¶ã®ããŒãžã§ã³ãäžèšã®ããã«èšå®ããŸããã webrtc.ecl.ntt.com SkyWay ã®æ¥ç¶ã¢ãã« SkyWay ã§ãããªé話ãå®è£
ããå Žåã2 çš®é¡ã®æ¥ç¶ã¢ãã«ããéžã³ãŸãã SkyWay é»è©±ã¢ãã« é»è©±ã®ããã« 1 察 1 ã§ã®ãããªéè©±ãæ³å®ããã¢ãã«ã§ãã Peer ã€ã³ã¹ã¿ã³ã¹(ã·ã°ããªã³ã°ãµãŒãã«ãã£ãŠçºè¡ãããäžæã® PeerID ãæã€)åå£«ã§æ¥ç¶ããŸãã æ¥ç¶ããããã«ã¯ãçžæã® PeerID ãå¿
èŠã«ãªããŸãã SkyWay ã«ãŒã ã¢ãã« åäžã«ãŒã å
ã®å
šãŠã® Peer ã§ãããªé話ããã¢ãã«ã§ãã ã«ãŒã åã䜿çšããŠåå ããŸããçžæã® PeerID ãç¥ãå¿
èŠã¯ãããŸããã ã«ãŒã å㯠API ããŒæ¯ã«ç¬ç«ããŠããŸãã ã«ãŒã ã®æ¥ç¶ã¿ã€ãã¯ãã«ã¡ãã·ã¥ã SFU ã® 2 çš®é¡ããéžã¹ãŸãã åå è
å
šå¡ãžã®ãã£ãããªã©ã®ããŒã¿éä¿¡ãã§ããŸãã ãžã§ãã¡ãã¬ãŒã® WEB 颿¥ã§ã¯ã ä»åŸã®æ©èœæ¡åŒµãæ³å®ããŠããã¡ãã®ã«ãŒã ã¢ãã«ãæ¡çšããŸããã ã«ãŒã ã®éä¿¡ã¿ã€ã ã«ãŒã ã«è€æ°äººãåå ããŠããå Žåãããããã®éä¿¡ãã©ãè¡ããããã¡ãã·ã¥ãš SFU ããéžã¶ããšãã§ããŸãã ãã«ã¡ãã·ã¥ ç»ååŒçšå
: https://webrtc.ecl.ntt.com/skyway/overview.html#_4-sfu%E3%82%B5%E3%83%BC%E3%83%90 å
šå¡ãçžäºã«éä¿¡ãè¡ããŸãã人æ°ãå¢ãããšã人æ°å端æ«ã®ãšã³ã³ãŒãè² è·ãšéä¿¡éãå¢å ããŸãã SFU SFU ã®å Žåãäžãã®æ¥ç¶ã¯ 1 æ¬ã«ãªãã®ã§ãã¡ãã·ã¥ããã端æ«ã®ãšã³ã³ãŒãè² è·ããéä¿¡éã®è»œæžãæåŸ
ã§ããŸãã ç»ååŒçšå
: https://webrtc.ecl.ntt.com/skyway/overview.html#_4-sfu%E3%82%B5%E3%83%BC%E3%83%90 éä¿¡æ¹åŒã®éã㯠SkyWay ãé èœããŠãããã®ã§ãjoinRoom æã® mode ã mesh ãã sfu ã«å€æŽããã ãã§åãæ¿ãããŸãã peer . joinRoom ( roomName , { mode: "mesh ã sfu ãæå®" , stream: mediaStream }); webrtc.ecl.ntt.com ãžã§ãã¡ãã¬ãŒã® WEB 颿¥ã§ã¯ã颿¥åå 人æ°ãèæ
®ã㊠mesh ã䜿çšããŠããŸãã å®è£
ã€ã¡ãŒãž const peer = new Peer ( peer_id , { key: api_key , }); peer . once ( "open" , () => { room . once ( "open" , () => { // ã«ãŒã åå åŸã«çºçããã€ãã³ã }); room . on ( "peerJoin" , ( peerId : string ) => { // ã«ãŒã ã«èª°ãåå ããå Žåã«çºçããã€ãã³ã }); room . on ( "stream" , ( stream : RoomStream ) => { // stream ãåããå Žåã«çºçããã€ãã³ã }); room . on ( "data" , ({ src , data }) => { // data ãåããå Žåã«çºçããã€ãã³ã }); }); peer . on ( "error" , ( error : Error ) => { // ãšã©ãŒçºçæã«çºçããã€ãã³ã }); peer . joinRoom ( roomName , { mode: "mesh" , stream: mediaStream }); room . close (); peer . disconnect (); Peer ãäœæã peer.joinRoom() ã§ã«ãŒã ã«åå room.stream ã€ãã³ãã§ä»ã®åå è
ã® stream ãåãåã room.data() ã§ãã£ãããªã©ãããŒã¿éä¿¡ãã§ãã room.close() ã§ã«ãŒã ããéåº ã SkyWay ã® JavaScript SDK ã䜿çšããåºæ¬çãªå®è£
ã«ãªããŸãã ãã®å®è£
ã« navigator.mediaDevices.getUserMedia() ã§ååŸãã stream ã joinRoom ã§æž¡ã steam ã€ãã³ãã§åãåã£ã stream ã video ã§åçãã ã远å ããã°ããªã³ã©ã€ã³ã§ã®ãããªé話ãå¯èœã«ãªããŸãã ã¹ããŒããã©ã³å¯Ÿå¿ PC ãšåæ§ã®ã³ãŒãã§ã»ãŒåäœããŸããããiOS ã Android 端æ«ã§ãæ©çš®äŸåãšæãããæåã®èª¿æ»ãšå¯Ÿå¿ã«æéãããããŸããããã®äžéšã玹ä»ããŸãã ã¿ããç§»åãããšæ åãæ ããªã çºçããåé¡ iOS12ãiOS13 ãªã©ã§ è€æ°ã®ã¿ããã²ãããå Žåã«ãæ åã®ååŸãã§ããªããªã ã¹ã¯ãªãŒã³ããã¯ããã®åŸ©åž°æã«ãæ åãååŸã§ããªããªã ãšããåé¡ãèµ·ããŸãããiOS14 ã§ã¯ã¿ãåãæ¿ãæã«æ åãååŸã§ããããã«ãªã£ãŠããŸããããã¹ã¯ãªãŒã³ããã¯ããã®åŸ©åž°æã¯æ åãååŸã§ããªããŸãŸã§ããã å¯Ÿå¿ ãã®å¯Ÿå¿ã¯ visibilitychange ã€ãã³ãã§ãã¿ãã®åãæ¿ããšãã¹ã¯ãªãŒã³ããã¯ããã®åŸ©åž°æã®ã€ãã³ããæŸã ååŸæžã® stream ã® track ã stop ãã stream ãåãçŽã SkyWay ã® room.replaceStream() ã§ãWebRTC ã§äœ¿çšããŠãã stream ãå·®ãæ¿ãã 以äžã®å®è£
ã«ããã察å¿ããŸããã __ document . addEventListener ( "visibilitychange" , async () => { if ( document . visibilityState !== "visible" ) { return ; } localMedia . getTracks (). forEach (( track : MediaStreamTrack ) => { track . stop (); }); const replaceStream = await navigator . mediaDevices . getUserMedia ({ video: true , audio: true , }); room . replaceStream ( replaceStream ); }); ã€ã€ãã³ã®æäœã§æ åãæ¢ãŸã çºçããåé¡ iOS12ãiOS13 ã§ã¯èµ·ãããŸããã§ããããiOS14 ã§ãªã³ã©ã€ã³é¢æ¥éäžã«ã€ã€ãã³ãã¹ããŒããã©ã³ããå€ããšãçžæåŽã®æ åãæ¢ãŸãããã«ãªããŸããã å¯Ÿå¿ ãããŸã§ã¯ãçžæã®æ åãšé³å£°ãåçããããã«ãvideo ã¿ã°ã« stream ãæž¡ããŠæ åãšé³å£°ãåçããŠããŸããã video : mute ã«ããŠæ åãåç audio : é³å£°ãåç ãšãé³å£°ãšåçãåãããšãããã€ã€ãã³ãå€ããŠã忢ããããšã¯ãªããªããŸããã å¶çŽèšå® getUserMedia ã§ååŸãã MediaStream ã¯ãå¶çŽãèšå®ããããšã§ããã€ã¹ã®æ¶è²»ãªãœãŒã¹ãæããããšãã§ããŸãã èšå®äŸ: navigator . mediaDevices . getUserMedia ({ audio: true , video: true }); â navigator . mediaDevices . getUserMedia ({ audio: true , video: { frameRate: 15 } }); å¶çŽãèšå®å¯èœãã©ããã®ç¢ºèª: getSupportedConstraints() ã§ã察å¿ããŠããå¶çŽåãååŸããããšãã§ããŸãã const supportedConstraints = navigator . mediaDevices . getSupportedConstraints (); 䜿çšããŠãããã©ãŠã¶ããã®å¶çŽã«å¯Ÿå¿ããŠãããã確èªãã察å¿ããŠããå Žåã®ã¿èšå®ãæå¹ã«ããŸãã äŸãã°ãã¹ããŒããã©ã³ã®å Žåã«ä»¥äžã®èšå®ãããã°ãã€ã³ã«ã¡ã©ã䜿çšãããã¬ãŒã ã¬ãŒãã 20 ã«æãã320x320 ã®è§£å床ã«å¶éããããšãã§ããŸãã const options = { facingMode: "user" , frameRate: 20 , width: 320 , height: 320 }; æå®ããå¶éãå¿
ã䜿çšãããä¿èšŒã¯ãªããæ©çš®äŸåã®åœ±é¿ãåããèšå®ã§ãããã®ã§ã察象ãšããŠããç°å¢ã«ããããŠæ€èšŒãšèª¿æŽãããå¿
èŠãããç¹ã«ã¯æ³šæãå¿
èŠã§ãã åäœæ€èšŒäžã«ãæ©çš®äŸåãšæãããæåãããäŸã玹ä»ããŸãã ãªãã·ã§ã³ æå frameRate æå®ãããšäžéš Android 端æ«ã§ä»¥äžã®æåãããã 1. Android 端æ«ã§é¢æ¥ã«åå ãã 2. iOS Safari ã§åå ãã 3. Android åŽã®æ åãšé³å£°ã Safari ã«éãããªã Safari ã§å
ã«åå ããå Žåã«ã¯åé¡ããªãã è§£å床æå® äŸ: width: 320, height: 320 frameRate ã ãæå®ããŠãããšãã«äžèšã®æåããã AndroidãiOS Safari ã®çµã¿åããã§åé¡ãèµ·ãããªããªã£ãã è§£å床ãæå®ãããšã€ã³ã«ã¡ã©ã§ã¯ãªãããªã¢ã«ã¡ã©ã䜿ã Android 端æ«ããã£ãã å¶çŽèšå®ã«ã€ããŠã¯ãçŸåšã調æŽäžã§ãã åèæ
å ± SkyWay Conference conf.webrtc.ecl.ntt.com github.com SkyWay ã®ã«ãŒã æ©èœã䜿çšãããã¢ç°å¢ã§ãã GitHub ã«ã³ãŒããå
¬éãããŠããã®ã§ãã«ãŒã ã䜿çšããå Žåã®æåãšå®è£
æ¹æ³ã確èªã§ããŸãã éçºãããŠãããšãå®è£
ã®åé¡ãªã®ããSkyWay ã® SDK ã®ä»æ§ãªã®ããç¹å®ã®ããã€ã¹ã§èµ·ããåé¡ãªã®ãã®åãåãã«æéããããããããã¡ãã®ç°å¢ãåèã«ãªããŸããã ãŸãšã SkyWay ã WebRTC ãšã°ã«ãŒãã§ã®ãããªé話ã®å®è£
ãçµ±åããŠæäŸããŠããããããéçºæã¯ãèªç€ŸãµãŒãã¹ãšããŠã® WEB 颿¥ã®æ©èœã«éäžããããšãã§ããŸããã ã¹ããŒããã©ã³ã®ãã©ãŠã¶å¯Ÿå¿ãšèª¿æ»ã«æéããããããšããããŸãããä»åŸãå©çšè
ããã®ãã£ãŒãããã¯ãåŸãªããæ¹åããŠãããããšæããŸãã ã¡ãã¬ãŒã§ã¯ãããŒãºã«ããããæ°æ©èœã®éçºã«ãåãå
¥ããŠããŸããå€ãã®å©çšè
ã«å®éã«äœ¿ããããµãŒãã¹ã®éçºãããŠã¿ãããšæã£ãæ¹ããã²ãæ°è»œã«ã話ããŸãããïŒ www.medley.jp
ããã«ã¡ã¯ãã¡ãã¬ãŒã®ãšã³ãžãã¢ã®å±±ç°ã§ããçŸåšãå»çä»è·æ±äººãµã€ãããžã§ãã¡ãã¬ãŒãã®ããŒã ã§éçºãæ
åœããŠããŸãã ä»åããžã§ãã¡ãã¬ãŒã®ç€Ÿå
ã¹ã¿ãããå©çšãã瀟å
ã·ã¹ãã ããªãã¥ãŒã¢ã«ããäºäŸãã玹ä»ããŸãã ãªãã¥ãŒã¢ã«å¯Ÿè±¡ã¯ããã¯ãšã³ãé åãå«ãŸããŸãããæ¬èšäºã§ã¯ããã³ããšã³ãã®è©±ãäžå¿ã«ã玹ä»ããŸãã ãŸããåŒç€Ÿãã¶ã€ããŒé
äºã以åæçš¿ãã ãã¶ã€ããŒããã¶ã€ã³ããŒã«ã䜿ããã«ãReact ã䜿ã£ãŠãã¶ã€ã³ãã話 ãé¢é£ããŠããã®ã§ããããããã°ããããŠã芧ãã ããã ãªãã¥ãŒã¢ã«ã®èæ¯ ç€Ÿå
ã·ã¹ãã ã§ã¯ãæ±äººãµã€ãããžã§ãã¡ãã¬ãŒããå©çšããæ±è·è
ã«é¢ããæ
å ±ãæ±è·è
ã®å¿åç¶æ³ã管çããŠããŸãã ååã®ãªãã¥ãŒã¢ã«ããæéãçµã¡ãè€éæ§ãé«ããªã£ãŠããŸããããã®è€éæ§ã«æ¯äŸããŠãæ°æ©èœã®è¿œå ãæ¹ä¿®ããããã®ã³ã¹ããé«ããªã£ãŠããŸããã ããã§äžèšã®èª²é¡ã解決ãããããç¶æ
管çãããããããã¹ãã³ãŒããæžãããããã¡ã³ãããã«ãªã¢ãŒããã¯ãã£ã«ãã¹ããªãã¥ãŒã¢ã«ã宿œããããšã«ããŸããã æ€èšŒæéãçµãŠãä»åã®ãªãã¥ãŒã¢ã«ã«ããããŠæ°èŠã«äœæãã API ã¯ãGraphQL ã«ãã£ãŠå®è£
ããããšã決ããŸããã åã·ã¹ãã ãæã€ããç»é¢ã«å¿
èŠãªããŒã¿ãæè»ã«éäžè¶³ãªãååŸã§ãããæåã§ããã¥ã¡ã³ãã«èœãšã蟌ãŸãªããŠãã¹ããŒããå®çŸ©ãããŠããã° API ã®ä»æ§ãç°¡åã«ææ¡ã§ãããçãã¡ãªãããšããŠæããããŸããã ç¹ã«ãGraphQL ãæã€åã·ã¹ãã ããTypeScriptãApolloãGraphQL Code Generator ã®ã©ã€ãã©ãªãçµã¿åãããããšã§ãAPI ã«æž¡ããã©ã¡ãŒã¿ããã¬ã¹ãã³ã¹ã«ãåãé©çšãããGraphQL ã¹ããŒãã®å€æŽã«ã¯ã©ã€ã¢ã³ãã®å®è£
ãæ¯èŒç容æã«è¿œåŸã§ããããšãã倧ããªãã€ã³ãã§ããã ããã³ããšã³ãã®æè¡çãªãªãã¥ãŒã¢ã«å
容 ä»åã¯ç¹ã«ããªãã¥ãŒã¢ã«ã«çšãããããã¬ãŒã ã¯ãŒã¯ãã©ã€ãã©ãªãApollo Client ãçšããç¶æ
管çããã¹ãã³ãŒãå®è£
ã«ããã Tips çãããããéšåçã«ã玹ä»ããŸãã æ¡çšãããã¬ãŒã ã¯ãŒã¯ãšäž»èŠã©ã€ãã©ãª æ¡çšã©ã€ãã©ãª 説æ Next.js React çšã®ãã¬ãŒã ã¯ãŒã¯ïŒãã€ã©ãŒãã¬ãŒãïŒ TypeScript JavaScript ã®ã¹ãŒããŒã»ããã§ãéçåä»ãèšèª React UI ãæ§ç¯ããããã®ã©ã€ãã©ãªïŒããŒãžã§ã³ 16.8.0 ã§ãªãªãŒã¹ããã hooks ãå
šé¢çã«äœ¿çšïŒ Apollo Client GraphQL API ã®ã¯ã©ã€ã¢ã³ãã§ãã¢ããªã±ãŒã·ã§ã³å
šäœã®ç¶æ
管çã宿œ GraphQL Code Generator GraphQL ã¹ããŒãããå®çŸ©ãã¡ã€ã«ïŒåãã«ã¹ã¿ã hooks çïŒãçæ emotion + Styled System CSS in JS ãšããŠå©çš formik + yup ãã©ãŒã ã®ãã«ã㌠+ ããªããŒã¿ãŒ Jest + React Testing Library ãã¹ãã³ãŒãå®è£
çšã®ããŒã«çŸ€ ESLint + Prettier ã«ãŒã«ã«åºã¥ããã³ãŒãã®éçè§£æ + ã¹ã¿ã€ãªã³ã° TypeScript ä»åã®ãªãã¥ãŒã¢ã«ã§æ±ããããããšã®äžã€ãšããŠããããªãæ¹åã»æ°èŠæ©èœè¿œå ãªã©ãããŠããäžã§ããœãããŠã§ã¢å質ãæ
ä¿ããããã®ãã¢ããªã±ãŒã·ã§ã³ã®å
ç¢ãããããŸããã ããã§ãããã³ããšã³ãåŽã®éçºèšèªãšããŠã¯ãããã°ã©ã ã³ãŒãå
ã§å®£èšãããåã«ãã£ãŠããšã©ãŒãæªç¶ã«é²ãã€ã€ãVSCode ãã¯ãããšãããšãã£ã¿ã®ã³ãŒãè£å®ã®æ©æµãåããããã¡ãªããçãèæ
®ã㊠TypeScript ã®æ¡çšã決ããŸããããŸããä»ã®ãããžã§ã¯ãã§ãæ¢ã« TypeScript ã¯éšåçã«å©çšãå§ããŠããäºæ
ããããéã« TypeScript ãæ¡çšããªãããšããéžæè¢ã¯ããŸãèããããŸããã§ããã React UI ãæ§ç¯ããããã®ã©ã€ãã©ãª/ãã¬ãŒã ã¯ãŒã¯ã¯ React ãæ¡çšããŸããããã¡ãããåŒç€Ÿã§ã¯å¥ãããžã§ã¯ãã§ React ãæ¢ã«å©çšãå§ããŠããããšããããåŠç¿ã³ã¹ãã®èгç¹ãããæ°ãã«ä»ã®ãã¬ãŒã ã¯ãŒã¯ãéžæããã¡ãªããã¯ã»ãŒç¡ãã£ãããã§ãããããããã®äºãå·®ãåŒãããšããŠã TypeScript ãš GraphQL ãšã®çžæ§ã®è¯ãã§ãReact ãåªå¢ã§ããã ç¹ã«ãReact ã®å Žåã¯ãGraphQL ã¹ããŒããããŒã¹ã«ãGraphQL Code Generator ã«ãã£ãŠåå®çŸ©ãã¡ã€ã«ã ãã§ã¯ãªããGraphQL API ãšã®ããåãã«äœ¿ããã«ã¹ã¿ã hooks ãçæããŠå©çšã§ãããšããç¹ãã倧ããªå©ç¹ãšããŠèããããŸããã Next.js ããã³ããšã³ãéçºç°å¢ãçŽ æ©ãæ§ç¯ããããããã€ã©ãŒãã¬ãŒããšã㊠Next.js ãæ¡çšããŸããã Next.js ã®å
·äœçãªæ¡çšãã€ã³ããšããŠã¯ãäž»ã«æ¬¡ã®ïŒç¹ã§ãã webpack ã«ãããããã³ãã«ãã³ã³ãã€ã«ãããããªããŒãçã®èšå®ã«æéãè²»ããããšãªããããžãã¹ããžãã¯ã®å®è£
ã«éäžã§ãã å¿
èŠãããã°ãnext.config.js ã§èšå®ãæ¡åŒµã§ãã CRAïŒCreate React AppïŒãšã¯ç°ãªããæ¡åŒµæ§ã«åªããŠãã pages é
äžã«çœ®ã React Component ã®ãã£ã¬ã¯ããªæ§æããèªåçã«ã«ãŒãã£ã³ã°ãšããŠå®çŸ©ããã ã«ãŒãã£ã³ã°ã«é¢ããèšèšäœæ¥ãäžèŠã«ãªã èªåã³ãŒãåå²çã«ããããã©ãŒãã³ã¹æé©åããããªã«è¡ã£ãŠããã React Component ã®åé¡ component ã¯å€§ããïŒã€ã«åé¡ãã src/components/app/ ãš src/components/ui/ ããããã®ãã£ã¬ã¯ããªã« component ã眮ããŠããŸããåé¡ã¯ä»¥äžã®åºæºã§è¡ãªããŸããã app : æ¬ã¢ããªã±ãŒã·ã§ã³åºæã§äœ¿çšãããæ³å®ã®ãã®ã§ãåå©çšæ§ãäœããå
·äœç㪠component ui : æ¬ã¢ããªã±ãŒã·ã§ã³å€ã§ã䜿çšå¯èœãªãåå©çšæ§ãé«ããæœè±¡ç㪠component 瀟å
åãã·ã¹ãã ã§ã¯ãããã®ã®ãMaterial-UI ã Ant Design çãã¯ãããšãããå€éšã® UI ã©ã€ãã©ãªã¯äœ¿çšãããã«ã¹ã¿ãã€ãºãããããããã«ãå
šãŠèªåã§äœæããŸããã app é
äžãš ui é
äžãã©ã¡ãã® component ãåºæ¬çã«ã¯ ã³ãã±ãŒã·ã§ã³ ã®èãæ¹ã§ãã¡ã€ã«ãæ§æããŠããŸãã äžè¬çã«ã¯ãããäžç·ã«å€æŽãããã¡ã€ã«ãè¿ãã«çœ®ããŠããã®ã¯è¯ãã¢ã€ãã£ã¢ã§ãã ãã®ååã¯ããã³ãã±ãŒã·ã§ã³ããšåŒã°ããŸãã ãã®èãæ¹ã§ãã¡ã€ã«ãæ§æããããšã§ãé¢é£ãããã¡ã€ã«ããŸãšãŸã£ãŠããŠãäœæ¥ããããããªããŸãã src/ components/ app/ partials/ ${ component å} / apollo.cache.ts apollo.query.graphql index.tsx index.test.tsx ... screens/ ${ component å} / apollo.cache.ts apollo.query.graphql index.tsx index.test.tsx ${å component å} / apollo.cache.ts apollo.query.graphql index.tsx index.test.tsx validation.ts src/components/app ãã£ã¬ã¯ããªé
äžã§ããã«ã partials ãš screens ã®ãã£ã¬ã¯ããªã§ component ãåããŠããŸãã screens ã«ã¯ãNext.js ã§ route ãšããŠæ±ããã src/pages é
äžã® component ãã import ããã component ãé
眮ãããŠããŸãã ç»é¢ã®ããªãšãŒã·ã§ã³ãå¢ãã床ã«ããã® screens ã«ãã¡ã€ã«ã远å ãããŠãããŸãã partials ã«ã¯ãapp é
äžã§è€æ°ã® component ããå©çšããã componentïŒç»é¢ããŸããã§å
±æããããã®çïŒãé
眮ããŠããŸãã screens ãš partials ããããçŽäžã® component ã§ãå¿
èŠã§ããã°é©å®ãcomponent ãåå²ããŠå component ãæã€æ§æã«ããŠããŸãã apollo.cache.ts ãš apollo.query.graphql ã«ã€ããŠã¯åŸè¿°ã®ç¶æ
管çã®è©±ã§ã玹ä»ããŸãã ç¶æ
管ç ã¢ããªã±ãŒã·ã§ã³ã®ç¶æ
管çã«ã€ããŠã¯ãã°ããŒãã«ã«ã¢ã¯ã»ã¹ã§ããç¶æ
ã®ç®¡çã«ã¯ Apollo Client ã® InMemoryCache ã«ãã cache æ©æ§ã§è¡ããç¹å®ã® component å
ã«éããŠãã屿çãªç¶æ
ã®ç®¡çã«ã¯ useState çã® React Hooks ã䜿ã£ãŠè¡ã£ãŠããŸãã ç¶æ
管çã®å¿
èŠæ§ãçããéãã¢ããªã±ãŒã·ã§ã³ã®è€éæ§ãäžããªãããã«ããªãã¹ã useState çã® hooks ãçšãã local state ã ãã§æžãŸããããªããã©ãããæ€èšããŸãã äŸãã°ãã¯ãªãã¯ãããšããããããŠã³ãªã¹ãã衚瀺ãããã»ã¬ã¯ãããã¯ã¹ã® component ã§ãããããããŠã³ãªã¹ãã®è¡šç€ºç¶æ
ããã® component å
ã ãã§æ±ãããã®ã§ããã° useState ãçšãã local state ã§ååã§ãããšèããããŸãã 芪åé¢ä¿ã§ã¯ãªã component å士ã§ã®ãããšããå¿
èŠã«ãªã£ãæãããµãŒãã®ããŒã¿ãšé¢é£ããå Žåçã§ãããŒã«ã«ã®ããŒã¿ãäžå
管çããŠãããæ¹ãè¯ããããªã±ãŒã¹ã§ã¯ãApollo Client ã® cache ãå©çšããŸãã Apollo Client Apollo ã«é¢é£ãããã¡ã€ã«ã®æ§æã«ã€ããŠã¯ä»¥äžã®éãã§ãã src/ apollo/ cache.ts client.ts types.ts withApollo.ts cache.ts : Apollo ã«ããã local state ã® initialState ãš resolver ãå
šç»é¢åãã®ãã¡ã€ã«ã§ãŸãšããŠãæçµçã« Next.js ã® src/pages/_app.tsx ã«æž¡ãããã«ãã component åºæã® local state ã«é¢ãã initialState ããã³ state ã® updater ãšãªã resolver 㯠component æ¯ã® apollo.cache.ts ã«ãŠãå¥éå®çŸ© client.ts : Apollo Client ã®ã€ã³ã¹ã¿ã³ã¹ãçæãããã¡ã€ã« types.ts : Apollo é¢é£ã®åå®çŸ©ãã¡ã€ã« withApollo.ts : Apllo Client ã® &lt;ApolloProvider /> ã§ã©ããããŠè¿ã Higher-Order Compoents(HOC) å®è£
ã«ã€ããŠã¯å²æããŸãããclient.ts ãš withApollo.ts ã«é¢ããŠã¯ãNext.js ã® exampleïŒ with-apollo ïŒçãåèã«ããŸããã ç»é¢åºæã® Apollo ã®ç¶æ
管çã«é¢ãããã¡ã€ã«ã¯ src/components/**/${component å}/ é
äžã«çœ®ããŠããŸãã ãã¡ããã³ãã±ãŒã·ã§ã³ã®èãæ¹ã§ãcomponent ã«é¢ããç¶æ
管çã¯è©²åœã® component ãšåãå Žæã«çœ®ãããšãæèããŠããŸãã src/ components/ app/ ${ component å} / apollo.cache.ts apollo.query.graphql apollo.schema.graphql apollo.cache.ts : component åºæã® Apollo ã«ããã local state ã® initialState ããã³ resolver ãå®çŸ©ãããã¡ã€ã« apollo.query.graphql : ã¯ãšãªãå®çŸ©ãããã¡ã€ã« apollo.schema.graphql : local state ã® GraphQL ã¹ããŒããå®çŸ©ãã¡ã€ã« ãã¡ã€ã«ã®åœåã«ã€ããŠã ãã£ã¬ã¯ããªéå±€ãã§ããã ãæ·±ãããããªã ã®ã§ã apollo çã«ãããã£ã¬ã¯ããªã¯èšããŠããŸããããApollo é¢é£ã®ãã¡ã€ã«çŸ€ãšããŠèªèã§ããããããã¡ã€ã«åã« apollo. ã®ãã¬ãã£ãã¯ã¹ãã€ããŠåœåããŠããŸãã Query ãš Mutation ã®å®è¡ã«ã€ã㊠GraphQL Code Generator ã®ãã©ã°ã€ã³ TypeScript React Apollo ãã€ã³ã¹ããŒã«ããŠãhooks ãçæããèšå®ã«ããäžã§ãcomponent æ¯ã«ãããã GraphQL ã®ã¹ããŒããšã¯ãšãªãèšè¿°ããã .graphql ãã¡ã€ã«ãããšã«ãGraphQL Code Generator ãçæããã«ã¹ã¿ã hooks ãå©çšããŸãã ãã¡ãã®ã«ã¹ã¿ã hooks ã React Component ã§å©çšããããšã§ãApollo Client çµç±ã§ GraphQL API ãšããŒã«ã«ã® Apollo cache ã«æ¥ç¶ããŠãããŒã¿ã®ããåããè¡ãããšãã§ããŸãã Query Query ã® hooks ã¯ïŒçš®é¡ãããå®è¡ããã¿ã€ãã³ã°ã«ãã£ãŠããããé©åãªæ¹ãéžãã§å®è¡ããŠããŸãã API å®è¡ã¿ã€ãã³ã° useQuery Component ã render ããããã¯ãšãªå®è¡ useLazyQuery ä»»æã®ã€ãã³ããããªã¬ãŒã«ããŠã¯ãšãªå®è¡ use***Query éåžžã§ããã° useQuery ã§ã¯ãšãªã®çµæã render ããŸãããGraphQL Code Generator ãå©çšããå Žåã¯ãããããã®ã¯ãšãªãã©ããããã«ã¹ã¿ã hooks ãçæãããã®ã§ã useQuery , useLazyQuery ããã®ãŸãŸäœ¿ãããšã¯ãããŸããã query AllPosts { allPosts { id title rating } } â ã®ãããªã¯ãšãªãçšæãããš src/__generated__/graphql.tsx ã«å¯ŸããŠã次ã®ãããªã«ã¹ã¿ã hooks ãåãšäžç·ã«çæãããèšå®ã«ããŠããŸãã // Apollo Client: 2.6.9ãGraphQL Code Generator: 1.15.0 ã®å Žåã®äŸ export function useAllPostsQuery ( baseOptions ?: ApolloReactHooks . QueryHookOptions & lt ; AllPostsQuery , AllPostsQueryVariables >) { return ApolloReactHooks . useQuery & lt ; AllPostsQuery , AllPostsQueryVariables >( AllPostsDocument , baseOptions ); } export function useAllPostsLazyQuery ( baseOptions ?: ApolloReactHooks . LazyQueryHookOptions & lt ; AllPostsQuery , AllPostsQueryVariables >) { return ApolloReactHooks . useLazyQuery & lt ; AllPostsQuery , AllPostsQueryVariables >( AllPostsDocument , baseOptions ); } React Component ã§ã¯çæãããã«ã¹ã¿ã hooks ãæ¬¡ã®ããã«åŒã³åºããŠãµãŒããŒããè¿ã£ãŠããçµæãåãåã£ãŠãããŒã¿åºåãããŒãã£ã³ã°ç¶æ
ã®ãã§ãã¯ããšã©ãŒãã³ããªã³ã°çãè¡ããŸãã const { data , loading , error } = useAllPostsQuery (); Mutation ããŒã¿ã®æžã蟌ã¿ã¯ useMutation ã§è¡ããŸãã Query åæ§ã GraphQL Code Generator ã«ãã£ãŠçæãããã«ã¹ã¿ã hooks use***Mutation ã䜿ã£ãŠããŸãã cache ã®æŽæ° Mutation ãè€æ°ãšã³ãã£ãã£ã®æŽæ°ããšã³ãã£ãã£ã®æ°èŠäœæãŸãã¯åé€ã®å ŽåãApollo Client ã® cache ã¯èªåæŽæ°ããããMutation ã®çµæãèªåçã« render ãããŸããã ãã®ãããªå Žåã§ãã useMutation ã® update option ã䜿ãã°ã cache ãªããžã§ã¯ããåŒæ°ã«åãã颿°ãèšå®ã§ããã®ã§ããã®é¢æ°å
ã§çŽæ¥ cache ãæŽæ°ã§ããŸãã ãŸãã update ã®ä»£ããã« refetchQueries ã® option ã䜿ã£ãŠãä»»æã® Query ãå®è¡ããŠãã·ã³ãã«ã« cache ãæŽæ°ããããšãã§ããŸãã äœãããã®æ¹æ³ã ãš Network éä¿¡ã«ãããªãŒããŒããããçºçããŸãã ãã®ãªãŒããŒããããç ç²ã«ããŠã§ãããµãŒããŒããããŒã¿ååŸããã Query ããããããªå Žåã«ã¯ããã® refetchQueries ãæå¹ã§ãã local state ã®ç®¡ç ããããã¯ç¹å®ã® component ã®ç¶æ
管çã local state ã䜿ã£ãŠã©ã®ããã«ç®¡çããŠãããããã説æããŠãããŸãã @client ã䜿ã£ã Query Next.js ã®ãããžã§ã¯ãã§ãlocal state ã®ç®¡çã Apollo Client ã§è¡ãå Žåã®äŸãšããŠã¯ã次ã®éãã§ãã ã¹ããŒãïŒ # src / components / app / Home / apollo . schema . graphql type Home { currentPostId: Int! } extend type Query { home: Home } ã¯ãšãªïŒ # src / components / app / Home / apollo . query . graphql query HomeCurrentPostId { home @ client { currentPostId } } ãã£ãã·ã¥ã®åæå€ïŒ // src/components/app/Home/apollo.cache.ts export const cache = { __typename: 'Home' , currentPostId: 0 , ..., }; // src/apollo/cache.ts const caches = { ..., home: home . cache , }; export { ..., caches , }; // src/pages/_app.tsx export const cache = new InMemoryCache (); ... const client = new ApolloClient ({ link , cache: cache . restore ( initialState || {}), resolvers , connectToDevTools: true , }); cache . writeData ({ data: caches }); GraphQL ã¯ãšãªãšã¹ããŒããå®çŸ©ãããŠããã° GraphQL Code Generator ã use***Query ã®ã³ãŒããçæããèšå®ã«ããŠããŸãã ããŒã«ã«ããŒã¿ã®å Žåãã¯ãšãªã§ @client ãã£ã¬ã¯ãã£ããã€ããŠããŒã«ã«ããŒã¿ã§ããããšãæç€ºããŸãã @client ã䜿ã£ã Mutation local state ã®æŽæ°ã GraphQL ã® Mutation ãšããŠè¡ãå Žåã®äŸãšããŠã¯ã次ã®éãã§ãã ã¹ããŒãïŒ # src / components / app / Home / apollo . schema . graphql type UpdateCurrentPostId { currentPostId: Int! } extend type Mutation { updateCurrentPostId(id: Int!): UpdateCurrentPostId } ã¯ãšãªïŒ # src / components / app / Home / apollo . query . graphql mutation UpdateCurrentPostId ( $id : Int !) { updateCurrentPostId ( id : $id ) @ client { currentPostId @ client } } resolverïŒ // src/components/app/Home/apollo.cache.ts const updateCurrentPostId : MutationResolvers [ "updateCurrentPostId" ] = ( _ , args , { cache } ) => { cache . writeData ({ data: { home: { __typename: "Home" , currentPostId: args . id , }, }, }); return null ; }; export const Mutation = { updateCurrentPostId , }; Query åæ§ã« @client ãã£ã¬ã¯ãã£ããã€ããŠããŒã«ã«ããŒã¿ã§ããããšãæç€ºããŸãã å®éã® Mutation ã®åŠçèªäœã¯ resolver ã®äžã« cache.writeData() ã䜿ã£ãŠèšè¿°ããŸãã Mutation ã®åœåã¯ã åè©+åè©ã®åœ¢åŒã§å¯èœãªéãæå³ã®ããå
·äœçãªååãã€ãã ããšãæèããŠããŸãã Apollo ã䜿ã£ãéçºã䟿å©ã«ããŠãããããŒã« Apollo Client ã䜿ã£ãŠéçºããéã¯ãããŒã«ã«ã® Apollo cache ã®ç¶æ
ããã¯ãšãªã詊ãã«å®è¡ããããã®ããŒã«ãšããŠãGoogle Chrome ã®æ¡åŒµæ©èœ Apollo Client Developer Tools ãéåžžã«äŸ¿å©ã§ãã ãã¡ãã®æ¡åŒµæ©èœã Chrome ã«ã€ã³ã¹ããŒã«ãããšãApollo Client ã䜿ã£ãŠ GraphQL API ã«ã¢ã¯ã»ã¹ãããµã€ãã«é·ç§»ããç¶æ
ã§ Chrome Dev Tools ãéããš Apollo ã®ã¿ãã衚瀺ãããŸããããã§ã¯ãšãªã®å®è¡ããAPI 仿§ã®ç¢ºèªãããŒã«ã«ã® Apollo cache ã®ç¢ºèªçãè¡ãããšãã§ããŸãã GraphQL é¢é£ã®ãã¹ãã³ãŒãã«ã€ã㊠Apollo Client ã䜿ã£ã React Component ã®éçºã§ãQuery ããã³ Mutation å®è¡ã®ãã¹ãã宿œããã«ã¯ããã¹ããã¬ãŒã ã¯ãŒã¯ã® Jestãreact-testing-library ãšããããŠãApollo å
¬åŒã§ã玹ä»ãããŠãã MockedProvider ãçšããæ¹æ³ãäžè¬çããšæããŸãã ã¯ãšãªãšã¯ãšãªã«å¯Ÿããã¬ã¹ãã³ã¹ãçµã¿åãããã¢ãã¯ããŒã¿ãçšæããŠãããApolloProvider ã®ä»£ããã« MockedProvider ã§ãã¹ã察象㮠component ãã©ããããããšã§ãAPI ãµãŒããŒã Network ç°å¢ã«äŸåãããã¢ãã¯ã§æå®ããã¯ãšãªããªã¯ãšã¹ãããããšãã¢ãã¯ã§ããã«å¯Ÿå¿ããããã«çšæããã¬ã¹ãã³ã¹ããŒã¿ã確å®ã«ååŸã§ããä»çµã¿ãäœããŸãã ãã®ä»çµã¿ãš react-testing-library ã䜿ã£ãŠãcomponent ã§ render ããã UI äžã®æäœãããªã¬ãŒã«ããŠå®è¡ããããã¯ãšãªã®ãã¹ããè¡ãããšãã§ããŸãã Query ã ãã§ã¯ãªã Mutation ãã¢ãã¯ããããšãã§ããŠã䟿å©ãªããŒã«ã§ã¯ãããŸããããã¹ãã±ãŒã¹æ¯ã«ã¢ãã¯ããŒã¿ã¯æåã§äœæããªããã°ãªããªãç¹ãããªããªã骚ãæããäœæ¥ã§ãã å®éã«ã¢ããªã±ãŒã·ã§ã³ãåãããŠããã¹ã察象㮠component ã render ããQuery ã«æž¡ããã variables ãã¬ã¹ãã³ã¹ã®å€ã Console ã«åºåãããã©ãŠã¶ã® Dev Tools äžã§äžåäžåãªããžã§ã¯ããã³ããŒããŠããšãã£ã¿ã«è²Œãä»ããããããäœæ¥ãçºçããŸãã AutoMockedProvider ã®äœæ ããã§ããããããã¹ãäœæãã¹ããŒã倿Žã®åºŠã«ãæåã§ã¢ãã¯ããŒã¿ãçšæããªããŠããGraphQL ã¹ããŒãã§å®çŸ©ãããŠããåãèŠãŠãèªåã§ã¯ãšãªã«å¯Ÿããã¬ã¹ãã³ã¹ãã¢ãã¯ããŠããã AutoMockedProvider ãã ãã¡ãã®èšäº ãåèã«ããŠäœæããŸããã MockedProvider ã®ä»£ããã«ãAutoMockedProvider ãçšããŠãã¹ã察象㮠Component ãã©ããããããšã§ãMockedProvider ã䜿ã£ãŠãã¹ãããŠããå
容ãšåããã¹ãã宿œã§ããŸãã MockedProvider ã䜿ã£ãŠæ¯åã¢ãã¯ããŒã¿ãçšæãããã¹ãã宿œããããšã«ç²ããŠããæ¹ã¯æ¯éãã詊ããã ããã ïŒç޹ä»å
ã®èšäºã§ã¯ã graphql-tools ã® makeExecutableSchema() ã«æž¡ã schemaSDL ã json ãã¡ã€ã«ã§å®çŸ©ãããŠããŸããã graphql-tag ã®ã©ã€ãã©ãªã䜵çšããã°ãgraphql ãã¡ã€ã«ã§ãåæ§ã« schemaSDL ãšããŠé©çšããããšãå¯èœã§ãïŒ ãªãã¥ãŒã¢ã«ãæ¯ãè¿ã£ãŠ ä»åã®ãªãã¥ãŒã¢ã«ã§ã¯ãGraphQLãTypeScriptãReact ãã»ããã§æ¡çšããããšã«ãããããã³ãåŽã§ã¯ GraphQL Code Generator ã䜿ã£ãŠããããããçšæããŠããã GraphQL ã¹ããŒããããTypeScript ã®åã ãã§ã¯ãªããReact ã® Hooks 颿°ãŸã§çæããŠå©çšã§ããããšããéçºå¹çã®åäžã«éåžžã«åœ±é¿ãäžãããšæããŸãã GraphQL API ã®ã¯ã©ã€ã¢ã³ãã§ãã¢ããªã±ãŒã·ã§ã³å
šäœã®ç¶æ
管çãè¡ã Apollo Client ã® cache æ©æ§ã®äœ¿ãæ¹çãäœåŸãããŸã§ã«ãåŠç¿ã³ã¹ãã¯æ±ºããŠãŒãã§ã¯ãããŸããã§ããããTypeScript ãš GraphQL ã®åã·ã¹ãã ã®æ©æµããã«ã«åããNext.js ã®ã¬ãŒã«ã«ã®ã£ãããåå®å
šãªéçºç°å¢ãæã«å
¥ããããšãã§ããŸããã æã
ãéçºè
ã®äœéšã ãã§ã¯ãªããä»åŸã®ãããã¯ãå
šäœãžã®çç£æ§ã«ãè¯ã圱é¿ãåãŒããŠããããšç¢ºä¿¡ããŠããŸãã ãããã« ã¡ãã¬ãŒã§ã¯ãšã³ãžãã¢ã»ãã¶ã€ããŒãç©æ¥µåéããŠããŸãã ããã¯ãããžãŒã掻çšããŠå»çãã«ã¹ã±ã¢ã®æªæ¥ãã€ããããšããããã·ã§ã³ã«å
±æãã課é¡è§£æ±ºãè¡ãããæ¹ã¯æ¯éããå¿åãã ããã åéã®äžèЧ | æ ªåŒäŒç€Ÿã¡ãã¬ãŒ ã¡ãã¬ãŒã®æ¡çšæ
å ±ã¯ãã¡ãããã確èªãã ããã www.medley.jp
ããã«ã¡ã¯ãã¡ãã¬ãŒã®ãšã³ãžãã¢ã®å±±ç°ã§ããçŸåšãå»çä»è·æ±äººãµã€ãããžã§ãã¡ãã¬ãŒãã®ããŒã ã§éçºãæ
åœããŠããŸãã ä»åããžã§ãã¡ãã¬ãŒã®ç€Ÿå
ã¹ã¿ãããå©çšãã瀟å
ã·ã¹ãã ããªãã¥ãŒã¢ã«ããäºäŸãã玹ä»ããŸãã ãªãã¥ãŒã¢ã«å¯Ÿè±¡ã¯ããã¯ãšã³ãé åãå«ãŸããŸãããæ¬èšäºã§ã¯ããã³ããšã³ãã®è©±ãäžå¿ã«ã玹ä»ããŸãã ãŸããåŒç€Ÿãã¶ã€ããŒé
äºã以åæçš¿ãã ãã¶ã€ããŒããã¶ã€ã³ããŒã«ã䜿ããã«ãReact ã䜿ã£ãŠãã¶ã€ã³ãã話 ãé¢é£ããŠããã®ã§ããããããã°ããããŠã芧ãã ããã ãªãã¥ãŒã¢ã«ã®èæ¯ ç€Ÿå
ã·ã¹ãã ã§ã¯ãæ±äººãµã€ãããžã§ãã¡ãã¬ãŒããå©çšããæ±è·è
ã«é¢ããæ
å ±ãæ±è·è
ã®å¿åç¶æ³ã管çããŠããŸãã ååã®ãªãã¥ãŒã¢ã«ããæéãçµã¡ãè€éæ§ãé«ããªã£ãŠããŸããããã®è€éæ§ã«æ¯äŸããŠãæ°æ©èœã®è¿œå ãæ¹ä¿®ããããã®ã³ã¹ããé«ããªã£ãŠããŸããã ããã§äžèšã®èª²é¡ã解決ãããããç¶æ
管çãããããããã¹ãã³ãŒããæžãããããã¡ã³ãããã«ãªã¢ãŒããã¯ãã£ã«ãã¹ããªãã¥ãŒã¢ã«ã宿œããããšã«ããŸããã æ€èšŒæéãçµãŠãä»åã®ãªãã¥ãŒã¢ã«ã«ããããŠæ°èŠã«äœæãã API ã¯ãGraphQL ã«ãã£ãŠå®è£
ããããšã決ããŸããã åã·ã¹ãã ãæã€ããç»é¢ã«å¿
èŠãªããŒã¿ãæè»ã«éäžè¶³ãªãååŸã§ãããæåã§ããã¥ã¡ã³ãã«èœãšã蟌ãŸãªããŠãã¹ããŒããå®çŸ©ãããŠããã° API ã®ä»æ§ãç°¡åã«ææ¡ã§ãããçãã¡ãªãããšããŠæããããŸããã ç¹ã«ãGraphQL ãæã€åã·ã¹ãã ããTypeScriptãApolloãGraphQL Code Generator ã®ã©ã€ãã©ãªãçµã¿åãããããšã§ãAPI ã«æž¡ããã©ã¡ãŒã¿ããã¬ã¹ãã³ã¹ã«ãåãé©çšãããGraphQL ã¹ããŒãã®å€æŽã«ã¯ã©ã€ã¢ã³ãã®å®è£
ãæ¯èŒç容æã«è¿œåŸã§ããããšãã倧ããªãã€ã³ãã§ããã ããã³ããšã³ãã®æè¡çãªãªãã¥ãŒã¢ã«å
容 ä»åã¯ç¹ã«ããªãã¥ãŒã¢ã«ã«çšãããããã¬ãŒã ã¯ãŒã¯ãã©ã€ãã©ãªãApollo Client ãçšããç¶æ
管çããã¹ãã³ãŒãå®è£
ã«ããã Tips çãããããéšåçã«ã玹ä»ããŸãã æ¡çšãããã¬ãŒã ã¯ãŒã¯ãšäž»èŠã©ã€ãã©ãª æ¡çšã©ã€ãã©ãª 説æ Next.js React çšã®ãã¬ãŒã ã¯ãŒã¯ïŒãã€ã©ãŒãã¬ãŒãïŒ TypeScript JavaScript ã®ã¹ãŒããŒã»ããã§ãéçåä»ãèšèª React UI ãæ§ç¯ããããã®ã©ã€ãã©ãªïŒããŒãžã§ã³ 16.8.0 ã§ãªãªãŒã¹ããã hooks ãå
šé¢çã«äœ¿çšïŒ Apollo Client GraphQL API ã®ã¯ã©ã€ã¢ã³ãã§ãã¢ããªã±ãŒã·ã§ã³å
šäœã®ç¶æ
管çã宿œ GraphQL Code Generator GraphQL ã¹ããŒãããå®çŸ©ãã¡ã€ã«ïŒåãã«ã¹ã¿ã hooks çïŒãçæ emotion + Styled System CSS in JS ãšããŠå©çš formik + yup ãã©ãŒã ã®ãã«ã㌠+ ããªããŒã¿ãŒ Jest + React Testing Library ãã¹ãã³ãŒãå®è£
çšã®ããŒã«çŸ€ ESLint + Prettier ã«ãŒã«ã«åºã¥ããã³ãŒãã®éçè§£æ + ã¹ã¿ã€ãªã³ã° TypeScript ä»åã®ãªãã¥ãŒã¢ã«ã§æ±ããããããšã®äžã€ãšããŠããããªãæ¹åã»æ°èŠæ©èœè¿œå ãªã©ãããŠããäžã§ããœãããŠã§ã¢å質ãæ
ä¿ããããã®ãã¢ããªã±ãŒã·ã§ã³ã®å
ç¢ãããããŸããã ããã§ãããã³ããšã³ãåŽã®éçºèšèªãšããŠã¯ãããã°ã©ã ã³ãŒãå
ã§å®£èšãããåã«ãã£ãŠããšã©ãŒãæªç¶ã«é²ãã€ã€ãVSCode ãã¯ãããšãããšãã£ã¿ã®ã³ãŒãè£å®ã®æ©æµãåããããã¡ãªããçãèæ
®ã㊠TypeScript ã®æ¡çšã決ããŸããããŸããä»ã®ãããžã§ã¯ãã§ãæ¢ã« TypeScript ã¯éšåçã«å©çšãå§ããŠããäºæ
ããããéã« TypeScript ãæ¡çšããªãããšããéžæè¢ã¯ããŸãèããããŸããã§ããã React UI ãæ§ç¯ããããã®ã©ã€ãã©ãª/ãã¬ãŒã ã¯ãŒã¯ã¯ React ãæ¡çšããŸããããã¡ãããåŒç€Ÿã§ã¯å¥ãããžã§ã¯ãã§ React ãæ¢ã«å©çšãå§ããŠããããšããããåŠç¿ã³ã¹ãã®èгç¹ãããæ°ãã«ä»ã®ãã¬ãŒã ã¯ãŒã¯ãéžæããã¡ãªããã¯ã»ãŒç¡ãã£ãããã§ãããããããã®äºãå·®ãåŒãããšããŠã TypeScript ãš GraphQL ãšã®çžæ§ã®è¯ãã§ãReact ãåªå¢ã§ããã ç¹ã«ãReact ã®å Žåã¯ãGraphQL ã¹ããŒããããŒã¹ã«ãGraphQL Code Generator ã«ãã£ãŠåå®çŸ©ãã¡ã€ã«ã ãã§ã¯ãªããGraphQL API ãšã®ããåãã«äœ¿ããã«ã¹ã¿ã hooks ãçæããŠå©çšã§ãããšããç¹ãã倧ããªå©ç¹ãšããŠèããããŸããã Next.js ããã³ããšã³ãéçºç°å¢ãçŽ æ©ãæ§ç¯ããããããã€ã©ãŒãã¬ãŒããšã㊠Next.js ãæ¡çšããŸããã Next.js ã®å
·äœçãªæ¡çšãã€ã³ããšããŠã¯ãäž»ã«æ¬¡ã®ïŒç¹ã§ãã webpack ã«ãããããã³ãã«ãã³ã³ãã€ã«ãããããªããŒãçã®èšå®ã«æéãè²»ããããšãªããããžãã¹ããžãã¯ã®å®è£
ã«éäžã§ãã å¿
èŠãããã°ãnext.config.js ã§èšå®ãæ¡åŒµã§ãã CRAïŒCreate React AppïŒãšã¯ç°ãªããæ¡åŒµæ§ã«åªããŠãã pages é
äžã«çœ®ã React Component ã®ãã£ã¬ã¯ããªæ§æããèªåçã«ã«ãŒãã£ã³ã°ãšããŠå®çŸ©ããã ã«ãŒãã£ã³ã°ã«é¢ããèšèšäœæ¥ãäžèŠã«ãªã èªåã³ãŒãåå²çã«ããããã©ãŒãã³ã¹æé©åããããªã«è¡ã£ãŠããã React Component ã®åé¡ component ã¯å€§ããïŒã€ã«åé¡ãã src/components/app/ ãš src/components/ui/ ããããã®ãã£ã¬ã¯ããªã« component ã眮ããŠããŸããåé¡ã¯ä»¥äžã®åºæºã§è¡ãªããŸããã app : æ¬ã¢ããªã±ãŒã·ã§ã³åºæã§äœ¿çšãããæ³å®ã®ãã®ã§ãåå©çšæ§ãäœããå
·äœç㪠component ui : æ¬ã¢ããªã±ãŒã·ã§ã³å€ã§ã䜿çšå¯èœãªãåå©çšæ§ãé«ããæœè±¡ç㪠component 瀟å
åãã·ã¹ãã ã§ã¯ãããã®ã®ãMaterial-UI ã Ant Design çãã¯ãããšãããå€éšã® UI ã©ã€ãã©ãªã¯äœ¿çšãããã«ã¹ã¿ãã€ãºãããããããã«ãå
šãŠèªåã§äœæããŸããã app é
äžãš ui é
äžãã©ã¡ãã® component ãåºæ¬çã«ã¯ ã³ãã±ãŒã·ã§ã³ ã®èãæ¹ã§ãã¡ã€ã«ãæ§æããŠããŸãã äžè¬çã«ã¯ãããäžç·ã«å€æŽãããã¡ã€ã«ãè¿ãã«çœ®ããŠããã®ã¯è¯ãã¢ã€ãã£ã¢ã§ãã ãã®ååã¯ããã³ãã±ãŒã·ã§ã³ããšåŒã°ããŸãã ãã®èãæ¹ã§ãã¡ã€ã«ãæ§æããããšã§ãé¢é£ãããã¡ã€ã«ããŸãšãŸã£ãŠããŠãäœæ¥ããããããªããŸãã src/ components/ app/ partials/ ${ component å} / apollo.cache.ts apollo.query.graphql index.tsx index.test.tsx ... screens/ ${ component å} / apollo.cache.ts apollo.query.graphql index.tsx index.test.tsx ${å component å} / apollo.cache.ts apollo.query.graphql index.tsx index.test.tsx validation.ts src/components/app ãã£ã¬ã¯ããªé
äžã§ããã«ã partials ãš screens ã®ãã£ã¬ã¯ããªã§ component ãåããŠããŸãã screens ã«ã¯ãNext.js ã§ route ãšããŠæ±ããã src/pages é
äžã® component ãã import ããã component ãé
眮ãããŠããŸãã ç»é¢ã®ããªãšãŒã·ã§ã³ãå¢ãã床ã«ããã® screens ã«ãã¡ã€ã«ã远å ãããŠãããŸãã partials ã«ã¯ãapp é
äžã§è€æ°ã® component ããå©çšããã componentïŒç»é¢ããŸããã§å
±æããããã®çïŒãé
眮ããŠããŸãã screens ãš partials ããããçŽäžã® component ã§ãå¿
èŠã§ããã°é©å®ãcomponent ãåå²ããŠå component ãæã€æ§æã«ããŠããŸãã apollo.cache.ts ãš apollo.query.graphql ã«ã€ããŠã¯åŸè¿°ã®ç¶æ
管çã®è©±ã§ã玹ä»ããŸãã ç¶æ
管ç ã¢ããªã±ãŒã·ã§ã³ã®ç¶æ
管çã«ã€ããŠã¯ãã°ããŒãã«ã«ã¢ã¯ã»ã¹ã§ããç¶æ
ã®ç®¡çã«ã¯ Apollo Client ã® InMemoryCache ã«ãã cache æ©æ§ã§è¡ããç¹å®ã® component å
ã«éããŠãã屿çãªç¶æ
ã®ç®¡çã«ã¯ useState çã® React Hooks ã䜿ã£ãŠè¡ã£ãŠããŸãã ç¶æ
管çã®å¿
èŠæ§ãçããéãã¢ããªã±ãŒã·ã§ã³ã®è€éæ§ãäžããªãããã«ããªãã¹ã useState çã® hooks ãçšãã local state ã ãã§æžãŸããããªããã©ãããæ€èšããŸãã äŸãã°ãã¯ãªãã¯ãããšããããããŠã³ãªã¹ãã衚瀺ãããã»ã¬ã¯ãããã¯ã¹ã® component ã§ãããããããŠã³ãªã¹ãã®è¡šç€ºç¶æ
ããã® component å
ã ãã§æ±ãããã®ã§ããã° useState ãçšãã local state ã§ååã§ãããšèããããŸãã 芪åé¢ä¿ã§ã¯ãªã component å士ã§ã®ãããšããå¿
èŠã«ãªã£ãæãããµãŒãã®ããŒã¿ãšé¢é£ããå Žåçã§ãããŒã«ã«ã®ããŒã¿ãäžå
管çããŠãããæ¹ãè¯ããããªã±ãŒã¹ã§ã¯ãApollo Client ã® cache ãå©çšããŸãã Apollo Client Apollo ã«é¢é£ãããã¡ã€ã«ã®æ§æã«ã€ããŠã¯ä»¥äžã®éãã§ãã src/ apollo/ cache.ts client.ts types.ts withApollo.ts cache.ts : Apollo ã«ããã local state ã® initialState ãš resolver ãå
šç»é¢åãã®ãã¡ã€ã«ã§ãŸãšããŠãæçµçã« Next.js ã® src/pages/_app.tsx ã«æž¡ãããã«ãã component åºæã® local state ã«é¢ãã initialState ããã³ state ã® updater ãšãªã resolver 㯠component æ¯ã® apollo.cache.ts ã«ãŠãå¥éå®çŸ© client.ts : Apollo Client ã®ã€ã³ã¹ã¿ã³ã¹ãçæãããã¡ã€ã« types.ts : Apollo é¢é£ã®åå®çŸ©ãã¡ã€ã« withApollo.ts : Apllo Client ã® &lt;ApolloProvider /> ã§ã©ããããŠè¿ã Higher-Order Compoents(HOC) å®è£
ã«ã€ããŠã¯å²æããŸãããclient.ts ãš withApollo.ts ã«é¢ããŠã¯ãNext.js ã® exampleïŒ with-apollo ïŒçãåèã«ããŸããã ç»é¢åºæã® Apollo ã®ç¶æ
管çã«é¢ãããã¡ã€ã«ã¯ src/components/**/${component å}/ é
äžã«çœ®ããŠããŸãã ãã¡ããã³ãã±ãŒã·ã§ã³ã®èãæ¹ã§ãcomponent ã«é¢ããç¶æ
管çã¯è©²åœã® component ãšåãå Žæã«çœ®ãããšãæèããŠããŸãã src/ components/ app/ ${ component å} / apollo.cache.ts apollo.query.graphql apollo.schema.graphql apollo.cache.ts : component åºæã® Apollo ã«ããã local state ã® initialState ããã³ resolver ãå®çŸ©ãããã¡ã€ã« apollo.query.graphql : ã¯ãšãªãå®çŸ©ãããã¡ã€ã« apollo.schema.graphql : local state ã® GraphQL ã¹ããŒããå®çŸ©ãã¡ã€ã« ãã¡ã€ã«ã®åœåã«ã€ããŠã ãã£ã¬ã¯ããªéå±€ãã§ããã ãæ·±ãããããªã ã®ã§ã apollo çã«ãããã£ã¬ã¯ããªã¯èšããŠããŸããããApollo é¢é£ã®ãã¡ã€ã«çŸ€ãšããŠèªèã§ããããããã¡ã€ã«åã« apollo. ã®ãã¬ãã£ãã¯ã¹ãã€ããŠåœåããŠããŸãã Query ãš Mutation ã®å®è¡ã«ã€ã㊠GraphQL Code Generator ã®ãã©ã°ã€ã³ TypeScript React Apollo ãã€ã³ã¹ããŒã«ããŠãhooks ãçæããèšå®ã«ããäžã§ãcomponent æ¯ã«ãããã GraphQL ã®ã¹ããŒããšã¯ãšãªãèšè¿°ããã .graphql ãã¡ã€ã«ãããšã«ãGraphQL Code Generator ãçæããã«ã¹ã¿ã hooks ãå©çšããŸãã ãã¡ãã®ã«ã¹ã¿ã hooks ã React Component ã§å©çšããããšã§ãApollo Client çµç±ã§ GraphQL API ãšããŒã«ã«ã® Apollo cache ã«æ¥ç¶ããŠãããŒã¿ã®ããåããè¡ãããšãã§ããŸãã Query Query ã® hooks ã¯ïŒçš®é¡ãããå®è¡ããã¿ã€ãã³ã°ã«ãã£ãŠããããé©åãªæ¹ãéžãã§å®è¡ããŠããŸãã API å®è¡ã¿ã€ãã³ã° useQuery Component ã render ããããã¯ãšãªå®è¡ useLazyQuery ä»»æã®ã€ãã³ããããªã¬ãŒã«ããŠã¯ãšãªå®è¡ use***Query éåžžã§ããã° useQuery ã§ã¯ãšãªã®çµæã render ããŸãããGraphQL Code Generator ãå©çšããå Žåã¯ãããããã®ã¯ãšãªãã©ããããã«ã¹ã¿ã hooks ãçæãããã®ã§ã useQuery , useLazyQuery ããã®ãŸãŸäœ¿ãããšã¯ãããŸããã query AllPosts { allPosts { id title rating } } â ã®ãããªã¯ãšãªãçšæãããš src/__generated__/graphql.tsx ã«å¯ŸããŠã次ã®ãããªã«ã¹ã¿ã hooks ãåãšäžç·ã«çæãããèšå®ã«ããŠããŸãã // Apollo Client: 2.6.9ãGraphQL Code Generator: 1.15.0 ã®å Žåã®äŸ export function useAllPostsQuery ( baseOptions ?: ApolloReactHooks . QueryHookOptions & lt ; AllPostsQuery , AllPostsQueryVariables >) { return ApolloReactHooks . useQuery & lt ; AllPostsQuery , AllPostsQueryVariables >( AllPostsDocument , baseOptions ); } export function useAllPostsLazyQuery ( baseOptions ?: ApolloReactHooks . LazyQueryHookOptions & lt ; AllPostsQuery , AllPostsQueryVariables >) { return ApolloReactHooks . useLazyQuery & lt ; AllPostsQuery , AllPostsQueryVariables >( AllPostsDocument , baseOptions ); } React Component ã§ã¯çæãããã«ã¹ã¿ã hooks ãæ¬¡ã®ããã«åŒã³åºããŠãµãŒããŒããè¿ã£ãŠããçµæãåãåã£ãŠãããŒã¿åºåãããŒãã£ã³ã°ç¶æ
ã®ãã§ãã¯ããšã©ãŒãã³ããªã³ã°çãè¡ããŸãã const { data , loading , error } = useAllPostsQuery (); Mutation ããŒã¿ã®æžã蟌ã¿ã¯ useMutation ã§è¡ããŸãã Query åæ§ã GraphQL Code Generator ã«ãã£ãŠçæãããã«ã¹ã¿ã hooks use***Mutation ã䜿ã£ãŠããŸãã cache ã®æŽæ° Mutation ãè€æ°ãšã³ãã£ãã£ã®æŽæ°ããšã³ãã£ãã£ã®æ°èŠäœæãŸãã¯åé€ã®å ŽåãApollo Client ã® cache ã¯èªåæŽæ°ããããMutation ã®çµæãèªåçã« render ãããŸããã ãã®ãããªå Žåã§ãã useMutation ã® update option ã䜿ãã°ã cache ãªããžã§ã¯ããåŒæ°ã«åãã颿°ãèšå®ã§ããã®ã§ããã®é¢æ°å
ã§çŽæ¥ cache ãæŽæ°ã§ããŸãã ãŸãã update ã®ä»£ããã« refetchQueries ã® option ã䜿ã£ãŠãä»»æã® Query ãå®è¡ããŠãã·ã³ãã«ã« cache ãæŽæ°ããããšãã§ããŸãã äœãããã®æ¹æ³ã ãš Network éä¿¡ã«ãããªãŒããŒããããçºçããŸãã ãã®ãªãŒããŒããããç ç²ã«ããŠã§ãããµãŒããŒããããŒã¿ååŸããã Query ããããããªå Žåã«ã¯ããã® refetchQueries ãæå¹ã§ãã local state ã®ç®¡ç ããããã¯ç¹å®ã® component ã®ç¶æ
管çã local state ã䜿ã£ãŠã©ã®ããã«ç®¡çããŠãããããã説æããŠãããŸãã @client ã䜿ã£ã Query Next.js ã®ãããžã§ã¯ãã§ãlocal state ã®ç®¡çã Apollo Client ã§è¡ãå Žåã®äŸãšããŠã¯ã次ã®éãã§ãã ã¹ããŒãïŒ # src / components / app / Home / apollo . schema . graphql type Home { currentPostId: Int! } extend type Query { home: Home } ã¯ãšãªïŒ # src / components / app / Home / apollo . query . graphql query HomeCurrentPostId { home @ client { currentPostId } } ãã£ãã·ã¥ã®åæå€ïŒ // src/components/app/Home/apollo.cache.ts export const cache = { __typename: 'Home' , currentPostId: 0 , ..., }; // src/apollo/cache.ts const caches = { ..., home: home . cache , }; export { ..., caches , }; // src/pages/_app.tsx export const cache = new InMemoryCache (); ... const client = new ApolloClient ({ link , cache: cache . restore ( initialState || {}), resolvers , connectToDevTools: true , }); cache . writeData ({ data: caches }); GraphQL ã¯ãšãªãšã¹ããŒããå®çŸ©ãããŠããã° GraphQL Code Generator ã use***Query ã®ã³ãŒããçæããèšå®ã«ããŠããŸãã ããŒã«ã«ããŒã¿ã®å Žåãã¯ãšãªã§ @client ãã£ã¬ã¯ãã£ããã€ããŠããŒã«ã«ããŒã¿ã§ããããšãæç€ºããŸãã @client ã䜿ã£ã Mutation local state ã®æŽæ°ã GraphQL ã® Mutation ãšããŠè¡ãå Žåã®äŸãšããŠã¯ã次ã®éãã§ãã ã¹ããŒãïŒ # src / components / app / Home / apollo . schema . graphql type UpdateCurrentPostId { currentPostId: Int! } extend type Mutation { updateCurrentPostId(id: Int!): UpdateCurrentPostId } ã¯ãšãªïŒ # src / components / app / Home / apollo . query . graphql mutation UpdateCurrentPostId ( $id : Int !) { updateCurrentPostId ( id : $id ) @ client { currentPostId @ client } } resolverïŒ // src/components/app/Home/apollo.cache.ts const updateCurrentPostId : MutationResolvers [ "updateCurrentPostId" ] = ( _ , args , { cache } ) => { cache . writeData ({ data: { home: { __typename: "Home" , currentPostId: args . id , }, }, }); return null ; }; export const Mutation = { updateCurrentPostId , }; Query åæ§ã« @client ãã£ã¬ã¯ãã£ããã€ããŠããŒã«ã«ããŒã¿ã§ããããšãæç€ºããŸãã å®éã® Mutation ã®åŠçèªäœã¯ resolver ã®äžã« cache.writeData() ã䜿ã£ãŠèšè¿°ããŸãã Mutation ã®åœåã¯ã åè©+åè©ã®åœ¢åŒã§å¯èœãªéãæå³ã®ããå
·äœçãªååãã€ãã ããšãæèããŠããŸãã Apollo ã䜿ã£ãéçºã䟿å©ã«ããŠãããããŒã« Apollo Client ã䜿ã£ãŠéçºããéã¯ãããŒã«ã«ã® Apollo cache ã®ç¶æ
ããã¯ãšãªã詊ãã«å®è¡ããããã®ããŒã«ãšããŠãGoogle Chrome ã®æ¡åŒµæ©èœ Apollo Client Developer Tools ãéåžžã«äŸ¿å©ã§ãã ãã¡ãã®æ¡åŒµæ©èœã Chrome ã«ã€ã³ã¹ããŒã«ãããšãApollo Client ã䜿ã£ãŠ GraphQL API ã«ã¢ã¯ã»ã¹ãããµã€ãã«é·ç§»ããç¶æ
ã§ Chrome Dev Tools ãéããš Apollo ã®ã¿ãã衚瀺ãããŸããããã§ã¯ãšãªã®å®è¡ããAPI 仿§ã®ç¢ºèªãããŒã«ã«ã® Apollo cache ã®ç¢ºèªçãè¡ãããšãã§ããŸãã GraphQL é¢é£ã®ãã¹ãã³ãŒãã«ã€ã㊠Apollo Client ã䜿ã£ã React Component ã®éçºã§ãQuery ããã³ Mutation å®è¡ã®ãã¹ãã宿œããã«ã¯ããã¹ããã¬ãŒã ã¯ãŒã¯ã® Jestãreact-testing-library ãšããããŠãApollo å
¬åŒã§ã玹ä»ãããŠãã MockedProvider ãçšããæ¹æ³ãäžè¬çããšæããŸãã ã¯ãšãªãšã¯ãšãªã«å¯Ÿããã¬ã¹ãã³ã¹ãçµã¿åãããã¢ãã¯ããŒã¿ãçšæããŠãããApolloProvider ã®ä»£ããã« MockedProvider ã§ãã¹ã察象㮠component ãã©ããããããšã§ãAPI ãµãŒããŒã Network ç°å¢ã«äŸåãããã¢ãã¯ã§æå®ããã¯ãšãªããªã¯ãšã¹ãããããšãã¢ãã¯ã§ããã«å¯Ÿå¿ããããã«çšæããã¬ã¹ãã³ã¹ããŒã¿ã確å®ã«ååŸã§ããä»çµã¿ãäœããŸãã ãã®ä»çµã¿ãš react-testing-library ã䜿ã£ãŠãcomponent ã§ render ããã UI äžã®æäœãããªã¬ãŒã«ããŠå®è¡ããããã¯ãšãªã®ãã¹ããè¡ãããšãã§ããŸãã Query ã ãã§ã¯ãªã Mutation ãã¢ãã¯ããããšãã§ããŠã䟿å©ãªããŒã«ã§ã¯ãããŸããããã¹ãã±ãŒã¹æ¯ã«ã¢ãã¯ããŒã¿ã¯æåã§äœæããªããã°ãªããªãç¹ãããªããªã骚ãæããäœæ¥ã§ãã å®éã«ã¢ããªã±ãŒã·ã§ã³ãåãããŠããã¹ã察象㮠component ã render ããQuery ã«æž¡ããã variables ãã¬ã¹ãã³ã¹ã®å€ã Console ã«åºåãããã©ãŠã¶ã® Dev Tools äžã§äžåäžåãªããžã§ã¯ããã³ããŒããŠããšãã£ã¿ã«è²Œãä»ããããããäœæ¥ãçºçããŸãã AutoMockedProvider ã®äœæ ããã§ããããããã¹ãäœæãã¹ããŒã倿Žã®åºŠã«ãæåã§ã¢ãã¯ããŒã¿ãçšæããªããŠããGraphQL ã¹ããŒãã§å®çŸ©ãããŠããåãèŠãŠãèªåã§ã¯ãšãªã«å¯Ÿããã¬ã¹ãã³ã¹ãã¢ãã¯ããŠããã AutoMockedProvider ãã ãã¡ãã®èšäº ãåèã«ããŠäœæããŸããã MockedProvider ã®ä»£ããã«ãAutoMockedProvider ãçšããŠãã¹ã察象㮠Component ãã©ããããããšã§ãMockedProvider ã䜿ã£ãŠãã¹ãããŠããå
容ãšåããã¹ãã宿œã§ããŸãã MockedProvider ã䜿ã£ãŠæ¯åã¢ãã¯ããŒã¿ãçšæãããã¹ãã宿œããããšã«ç²ããŠããæ¹ã¯æ¯éãã詊ããã ããã ïŒç޹ä»å
ã®èšäºã§ã¯ã graphql-tools ã® makeExecutableSchema() ã«æž¡ã schemaSDL ã json ãã¡ã€ã«ã§å®çŸ©ãããŠããŸããã graphql-tag ã®ã©ã€ãã©ãªã䜵çšããã°ãgraphql ãã¡ã€ã«ã§ãåæ§ã« schemaSDL ãšããŠé©çšããããšãå¯èœã§ãïŒ ãªãã¥ãŒã¢ã«ãæ¯ãè¿ã£ãŠ ä»åã®ãªãã¥ãŒã¢ã«ã§ã¯ãGraphQLãTypeScriptãReact ãã»ããã§æ¡çšããããšã«ãããããã³ãåŽã§ã¯ GraphQL Code Generator ã䜿ã£ãŠããããããçšæããŠããã GraphQL ã¹ããŒããããTypeScript ã®åã ãã§ã¯ãªããReact ã® Hooks 颿°ãŸã§çæããŠå©çšã§ããããšããéçºå¹çã®åäžã«éåžžã«åœ±é¿ãäžãããšæããŸãã GraphQL API ã®ã¯ã©ã€ã¢ã³ãã§ãã¢ããªã±ãŒã·ã§ã³å
šäœã®ç¶æ
管çãè¡ã Apollo Client ã® cache æ©æ§ã®äœ¿ãæ¹çãäœåŸãããŸã§ã«ãåŠç¿ã³ã¹ãã¯æ±ºããŠãŒãã§ã¯ãããŸããã§ããããTypeScript ãš GraphQL ã®åã·ã¹ãã ã®æ©æµããã«ã«åããNext.js ã®ã¬ãŒã«ã«ã®ã£ãããåå®å
šãªéçºç°å¢ãæã«å
¥ããããšãã§ããŸããã æã
ãéçºè
ã®äœéšã ãã§ã¯ãªããä»åŸã®ãããã¯ãå
šäœãžã®çç£æ§ã«ãè¯ã圱é¿ãåãŒããŠããããšç¢ºä¿¡ããŠããŸãã ãããã« ã¡ãã¬ãŒã§ã¯ãšã³ãžãã¢ã»ãã¶ã€ããŒãç©æ¥µåéããŠããŸãã ããã¯ãããžãŒã掻çšããŠå»çãã«ã¹ã±ã¢ã®æªæ¥ãã€ããããšããããã·ã§ã³ã«å
±æãã課é¡è§£æ±ºãè¡ãããæ¹ã¯æ¯éããå¿åãã ããã https://www.medley.jp/jobs/
ããã«ã¡ã¯ãã¡ãã¬ãŒã®ãšã³ãžãã¢ã®å±±ç°ã§ããçŸåšãå»çä»è·æ±äººãµã€ãããžã§ãã¡ãã¬ãŒãã®ããŒã ã§éçºãæ
åœããŠããŸãã ä»åããžã§ãã¡ãã¬ãŒã®ç€Ÿå
ã¹ã¿ãããå©çšãã瀟å
ã·ã¹ãã ããªãã¥ãŒã¢ã«ããäºäŸãã玹ä»ããŸãã ãªãã¥ãŒã¢ã«å¯Ÿè±¡ã¯ããã¯ãšã³ãé åãå«ãŸããŸãããæ¬èšäºã§ã¯ããã³ããšã³ãã®è©±ãäžå¿ã«ã玹ä»ããŸãã ãŸããåŒç€Ÿãã¶ã€ããŒé
äºã以åæçš¿ãã ãã¶ã€ããŒããã¶ã€ã³ããŒã«ã䜿ããã«ãReact ã䜿ã£ãŠãã¶ã€ã³ãã話 ãé¢é£ããŠããã®ã§ããããããã°ããããŠã芧ãã ããã ãªãã¥ãŒã¢ã«ã®èæ¯ ç€Ÿå
ã·ã¹ãã ã§ã¯ãæ±äººãµã€ãããžã§ãã¡ãã¬ãŒããå©çšããæ±è·è
ã«é¢ããæ
å ±ãæ±è·è
ã®å¿åç¶æ³ã管çããŠããŸãã ååã®ãªãã¥ãŒã¢ã«ããæéãçµã¡ãè€éæ§ãé«ããªã£ãŠããŸããããã®è€éæ§ã«æ¯äŸããŠãæ°æ©èœã®è¿œå ãæ¹ä¿®ããããã®ã³ã¹ããé«ããªã£ãŠããŸããã ããã§äžèšã®èª²é¡ã解決ãããããç¶æ
管çãããããããã¹ãã³ãŒããæžãããããã¡ã³ãããã«ãªã¢ãŒããã¯ãã£ã«ãã¹ããªãã¥ãŒã¢ã«ã宿œããããšã«ããŸããã æ€èšŒæéãçµãŠãä»åã®ãªãã¥ãŒã¢ã«ã«ããããŠæ°èŠã«äœæãã API ã¯ãGraphQL ã«ãã£ãŠå®è£
ããããšã決ããŸããã åã·ã¹ãã ãæã€ããç»é¢ã«å¿
èŠãªããŒã¿ãæè»ã«éäžè¶³ãªãååŸã§ãããæåã§ããã¥ã¡ã³ãã«èœãšã蟌ãŸãªããŠãã¹ããŒããå®çŸ©ãããŠããã° API ã®ä»æ§ãç°¡åã«ææ¡ã§ãããçãã¡ãªãããšããŠæããããŸããã ç¹ã«ãGraphQL ãæã€åã·ã¹ãã ããTypeScriptãApolloãGraphQL Code Generator ã®ã©ã€ãã©ãªãçµã¿åãããããšã§ãAPI ã«æž¡ããã©ã¡ãŒã¿ããã¬ã¹ãã³ã¹ã«ãåãé©çšãããGraphQL ã¹ããŒãã®å€æŽã«ã¯ã©ã€ã¢ã³ãã®å®è£
ãæ¯èŒç容æã«è¿œåŸã§ããããšãã倧ããªãã€ã³ãã§ããã ããã³ããšã³ãã®æè¡çãªãªãã¥ãŒã¢ã«å
容 ä»åã¯ç¹ã«ããªãã¥ãŒã¢ã«ã«çšãããããã¬ãŒã ã¯ãŒã¯ãã©ã€ãã©ãªãApollo Client ãçšããç¶æ
管çããã¹ãã³ãŒãå®è£
ã«ããã Tips çãããããéšåçã«ã玹ä»ããŸãã æ¡çšãããã¬ãŒã ã¯ãŒã¯ãšäž»èŠã©ã€ãã©ãª æ¡çšã©ã€ãã©ãª 説æ Next.js React çšã®ãã¬ãŒã ã¯ãŒã¯ïŒãã€ã©ãŒãã¬ãŒãïŒ TypeScript JavaScript ã®ã¹ãŒããŒã»ããã§ãéçåä»ãèšèª React UI ãæ§ç¯ããããã®ã©ã€ãã©ãªïŒããŒãžã§ã³ 16.8.0 ã§ãªãªãŒã¹ããã hooks ãå
šé¢çã«äœ¿çšïŒ Apollo Client GraphQL API ã®ã¯ã©ã€ã¢ã³ãã§ãã¢ããªã±ãŒã·ã§ã³å
šäœã®ç¶æ
管çã宿œ GraphQL Code Generator GraphQL ã¹ããŒãããå®çŸ©ãã¡ã€ã«ïŒåãã«ã¹ã¿ã hooks çïŒãçæ emotion + Styled System CSS in JS ãšããŠå©çš formik + yup ãã©ãŒã ã®ãã«ã㌠+ ããªããŒã¿ãŒ Jest + React Testing Library ãã¹ãã³ãŒãå®è£
çšã®ããŒã«çŸ€ ESLint + Prettier ã«ãŒã«ã«åºã¥ããã³ãŒãã®éçè§£æ + ã¹ã¿ã€ãªã³ã° TypeScript ä»åã®ãªãã¥ãŒã¢ã«ã§æ±ããããããšã®äžã€ãšããŠããããªãæ¹åã»æ°èŠæ©èœè¿œå ãªã©ãããŠããäžã§ããœãããŠã§ã¢å質ãæ
ä¿ããããã®ãã¢ããªã±ãŒã·ã§ã³ã®å
ç¢ãããããŸããã ããã§ãããã³ããšã³ãåŽã®éçºèšèªãšããŠã¯ãããã°ã©ã ã³ãŒãå
ã§å®£èšãããåã«ãã£ãŠããšã©ãŒãæªç¶ã«é²ãã€ã€ãVSCode ãã¯ãããšãããšãã£ã¿ã®ã³ãŒãè£å®ã®æ©æµãåããããã¡ãªããçãèæ
®ã㊠TypeScript ã®æ¡çšã決ããŸããããŸããä»ã®ãããžã§ã¯ãã§ãæ¢ã« TypeScript ã¯éšåçã«å©çšãå§ããŠããäºæ
ããããéã« TypeScript ãæ¡çšããªãããšããéžæè¢ã¯ããŸãèããããŸããã§ããã React UI ãæ§ç¯ããããã®ã©ã€ãã©ãª/ãã¬ãŒã ã¯ãŒã¯ã¯ React ãæ¡çšããŸããããã¡ãããåŒç€Ÿã§ã¯å¥ãããžã§ã¯ãã§ React ãæ¢ã«å©çšãå§ããŠããããšããããåŠç¿ã³ã¹ãã®èгç¹ãããæ°ãã«ä»ã®ãã¬ãŒã ã¯ãŒã¯ãéžæããã¡ãªããã¯ã»ãŒç¡ãã£ãããã§ãããããããã®äºãå·®ãåŒãããšããŠã TypeScript ãš GraphQL ãšã®çžæ§ã®è¯ãã§ãReact ãåªå¢ã§ããã ç¹ã«ãReact ã®å Žåã¯ãGraphQL ã¹ããŒããããŒã¹ã«ãGraphQL Code Generator ã«ãã£ãŠåå®çŸ©ãã¡ã€ã«ã ãã§ã¯ãªããGraphQL API ãšã®ããåãã«äœ¿ããã«ã¹ã¿ã hooks ãçæããŠå©çšã§ãããšããç¹ãã倧ããªå©ç¹ãšããŠèããããŸããã Next.js ããã³ããšã³ãéçºç°å¢ãçŽ æ©ãæ§ç¯ããããããã€ã©ãŒãã¬ãŒããšã㊠Next.js ãæ¡çšããŸããã Next.js ã®å
·äœçãªæ¡çšãã€ã³ããšããŠã¯ãäž»ã«æ¬¡ã®ïŒç¹ã§ãã webpack ã«ãããããã³ãã«ãã³ã³ãã€ã«ãããããªããŒãçã®èšå®ã«æéãè²»ããããšãªããããžãã¹ããžãã¯ã®å®è£
ã«éäžã§ãã å¿
èŠãããã°ãnext.config.js ã§èšå®ãæ¡åŒµã§ãã CRAïŒCreate React AppïŒãšã¯ç°ãªããæ¡åŒµæ§ã«åªããŠãã pages é
äžã«çœ®ã React Component ã®ãã£ã¬ã¯ããªæ§æããèªåçã«ã«ãŒãã£ã³ã°ãšããŠå®çŸ©ããã ã«ãŒãã£ã³ã°ã«é¢ããèšèšäœæ¥ãäžèŠã«ãªã èªåã³ãŒãåå²çã«ããããã©ãŒãã³ã¹æé©åããããªã«è¡ã£ãŠããã React Component ã®åé¡ component ã¯å€§ããïŒã€ã«åé¡ãã src/components/app/ ãš src/components/ui/ ããããã®ãã£ã¬ã¯ããªã« component ã眮ããŠããŸããåé¡ã¯ä»¥äžã®åºæºã§è¡ãªããŸããã app : æ¬ã¢ããªã±ãŒã·ã§ã³åºæã§äœ¿çšãããæ³å®ã®ãã®ã§ãåå©çšæ§ãäœããå
·äœç㪠component ui : æ¬ã¢ããªã±ãŒã·ã§ã³å€ã§ã䜿çšå¯èœãªãåå©çšæ§ãé«ããæœè±¡ç㪠component 瀟å
åãã·ã¹ãã ã§ã¯ãããã®ã®ãMaterial-UI ã Ant Design çãã¯ãããšãããå€éšã® UI ã©ã€ãã©ãªã¯äœ¿çšãããã«ã¹ã¿ãã€ãºãããããããã«ãå
šãŠèªåã§äœæããŸããã app é
äžãš ui é
äžãã©ã¡ãã® component ãåºæ¬çã«ã¯ ã³ãã±ãŒã·ã§ã³ ã®èãæ¹ã§ãã¡ã€ã«ãæ§æããŠããŸãã äžè¬çã«ã¯ãããäžç·ã«å€æŽãããã¡ã€ã«ãè¿ãã«çœ®ããŠããã®ã¯è¯ãã¢ã€ãã£ã¢ã§ãã ãã®ååã¯ããã³ãã±ãŒã·ã§ã³ããšåŒã°ããŸãã ãã®èãæ¹ã§ãã¡ã€ã«ãæ§æããããšã§ãé¢é£ãããã¡ã€ã«ããŸãšãŸã£ãŠããŠãäœæ¥ããããããªããŸãã src/ components/ app/ partials/ ${ component å} / apollo.cache.ts apollo.query.graphql index.tsx index.test.tsx ... screens/ ${ component å} / apollo.cache.ts apollo.query.graphql index.tsx index.test.tsx ${å component å} / apollo.cache.ts apollo.query.graphql index.tsx index.test.tsx validation.ts src/components/app ãã£ã¬ã¯ããªé
äžã§ããã«ã partials ãš screens ã®ãã£ã¬ã¯ããªã§ component ãåããŠããŸãã screens ã«ã¯ãNext.js ã§ route ãšããŠæ±ããã src/pages é
äžã® component ãã import ããã component ãé
眮ãããŠããŸãã ç»é¢ã®ããªãšãŒã·ã§ã³ãå¢ãã床ã«ããã® screens ã«ãã¡ã€ã«ã远å ãããŠãããŸãã partials ã«ã¯ãapp é
äžã§è€æ°ã® component ããå©çšããã componentïŒç»é¢ããŸããã§å
±æããããã®çïŒãé
眮ããŠããŸãã screens ãš partials ããããçŽäžã® component ã§ãå¿
èŠã§ããã°é©å®ãcomponent ãåå²ããŠå component ãæã€æ§æã«ããŠããŸãã apollo.cache.ts ãš apollo.query.graphql ã«ã€ããŠã¯åŸè¿°ã®ç¶æ
管çã®è©±ã§ã玹ä»ããŸãã ç¶æ
管ç ã¢ããªã±ãŒã·ã§ã³ã®ç¶æ
管çã«ã€ããŠã¯ãã°ããŒãã«ã«ã¢ã¯ã»ã¹ã§ããç¶æ
ã®ç®¡çã«ã¯ Apollo Client ã® InMemoryCache ã«ãã cache æ©æ§ã§è¡ããç¹å®ã® component å
ã«éããŠãã屿çãªç¶æ
ã®ç®¡çã«ã¯ useState çã® React Hooks ã䜿ã£ãŠè¡ã£ãŠããŸãã ç¶æ
管çã®å¿
èŠæ§ãçããéãã¢ããªã±ãŒã·ã§ã³ã®è€éæ§ãäžããªãããã«ããªãã¹ã useState çã® hooks ãçšãã local state ã ãã§æžãŸããããªããã©ãããæ€èšããŸãã äŸãã°ãã¯ãªãã¯ãããšããããããŠã³ãªã¹ãã衚瀺ãããã»ã¬ã¯ãããã¯ã¹ã® component ã§ãããããããŠã³ãªã¹ãã®è¡šç€ºç¶æ
ããã® component å
ã ãã§æ±ãããã®ã§ããã° useState ãçšãã local state ã§ååã§ãããšèããããŸãã 芪åé¢ä¿ã§ã¯ãªã component å士ã§ã®ãããšããå¿
èŠã«ãªã£ãæãããµãŒãã®ããŒã¿ãšé¢é£ããå Žåçã§ãããŒã«ã«ã®ããŒã¿ãäžå
管çããŠãããæ¹ãè¯ããããªã±ãŒã¹ã§ã¯ãApollo Client ã® cache ãå©çšããŸãã Apollo Client Apollo ã«é¢é£ãããã¡ã€ã«ã®æ§æã«ã€ããŠã¯ä»¥äžã®éãã§ãã src/ apollo/ cache.ts client.ts types.ts withApollo.ts cache.ts : Apollo ã«ããã local state ã® initialState ãš resolver ãå
šç»é¢åãã®ãã¡ã€ã«ã§ãŸãšããŠãæçµçã« Next.js ã® src/pages/_app.tsx ã«æž¡ãããã«ãã component åºæã® local state ã«é¢ãã initialState ããã³ state ã® updater ãšãªã resolver 㯠component æ¯ã® apollo.cache.ts ã«ãŠãå¥éå®çŸ© client.ts : Apollo Client ã®ã€ã³ã¹ã¿ã³ã¹ãçæãããã¡ã€ã« types.ts : Apollo é¢é£ã®åå®çŸ©ãã¡ã€ã« withApollo.ts : Apllo Client ã® &lt;ApolloProvider /> ã§ã©ããããŠè¿ã Higher-Order Compoents(HOC) å®è£
ã«ã€ããŠã¯å²æããŸãããclient.ts ãš withApollo.ts ã«é¢ããŠã¯ãNext.js ã® exampleïŒ with-apollo ïŒçãåèã«ããŸããã ç»é¢åºæã® Apollo ã®ç¶æ
管çã«é¢ãããã¡ã€ã«ã¯ src/components/**/${component å}/ é
äžã«çœ®ããŠããŸãã ãã¡ããã³ãã±ãŒã·ã§ã³ã®èãæ¹ã§ãcomponent ã«é¢ããç¶æ
管çã¯è©²åœã® component ãšåãå Žæã«çœ®ãããšãæèããŠããŸãã src/ components/ app/ ${ component å} / apollo.cache.ts apollo.query.graphql apollo.schema.graphql apollo.cache.ts : component åºæã® Apollo ã«ããã local state ã® initialState ããã³ resolver ãå®çŸ©ãããã¡ã€ã« apollo.query.graphql : ã¯ãšãªãå®çŸ©ãããã¡ã€ã« apollo.schema.graphql : local state ã® GraphQL ã¹ããŒããå®çŸ©ãã¡ã€ã« ãã¡ã€ã«ã®åœåã«ã€ããŠã ãã£ã¬ã¯ããªéå±€ãã§ããã ãæ·±ãããããªã ã®ã§ã apollo çã«ãããã£ã¬ã¯ããªã¯èšããŠããŸããããApollo é¢é£ã®ãã¡ã€ã«çŸ€ãšããŠèªèã§ããããããã¡ã€ã«åã« apollo. ã®ãã¬ãã£ãã¯ã¹ãã€ããŠåœåããŠããŸãã Query ãš Mutation ã®å®è¡ã«ã€ã㊠GraphQL Code Generator ã®ãã©ã°ã€ã³ TypeScript React Apollo ãã€ã³ã¹ããŒã«ããŠãhooks ãçæããèšå®ã«ããäžã§ãcomponent æ¯ã«ãããã GraphQL ã®ã¹ããŒããšã¯ãšãªãèšè¿°ããã .graphql ãã¡ã€ã«ãããšã«ãGraphQL Code Generator ãçæããã«ã¹ã¿ã hooks ãå©çšããŸãã ãã¡ãã®ã«ã¹ã¿ã hooks ã React Component ã§å©çšããããšã§ãApollo Client çµç±ã§ GraphQL API ãšããŒã«ã«ã® Apollo cache ã«æ¥ç¶ããŠãããŒã¿ã®ããåããè¡ãããšãã§ããŸãã Query Query ã® hooks ã¯ïŒçš®é¡ãããå®è¡ããã¿ã€ãã³ã°ã«ãã£ãŠããããé©åãªæ¹ãéžãã§å®è¡ããŠããŸãã API å®è¡ã¿ã€ãã³ã° useQuery Component ã render ããããã¯ãšãªå®è¡ useLazyQuery ä»»æã®ã€ãã³ããããªã¬ãŒã«ããŠã¯ãšãªå®è¡ use***Query éåžžã§ããã° useQuery ã§ã¯ãšãªã®çµæã render ããŸãããGraphQL Code Generator ãå©çšããå Žåã¯ãããããã®ã¯ãšãªãã©ããããã«ã¹ã¿ã hooks ãçæãããã®ã§ã useQuery , useLazyQuery ããã®ãŸãŸäœ¿ãããšã¯ãããŸããã query AllPosts { allPosts { id title rating } } â ã®ãããªã¯ãšãªãçšæãããš src/__generated__/graphql.tsx ã«å¯ŸããŠã次ã®ãããªã«ã¹ã¿ã hooks ãåãšäžç·ã«çæãããèšå®ã«ããŠããŸãã // Apollo Client: 2.6.9ãGraphQL Code Generator: 1.15.0 ã®å Žåã®äŸ export function useAllPostsQuery ( baseOptions ?: ApolloReactHooks . QueryHookOptions & lt ; AllPostsQuery , AllPostsQueryVariables >) { return ApolloReactHooks . useQuery & lt ; AllPostsQuery , AllPostsQueryVariables >( AllPostsDocument , baseOptions ); } export function useAllPostsLazyQuery ( baseOptions ?: ApolloReactHooks . LazyQueryHookOptions & lt ; AllPostsQuery , AllPostsQueryVariables >) { return ApolloReactHooks . useLazyQuery & lt ; AllPostsQuery , AllPostsQueryVariables >( AllPostsDocument , baseOptions ); } React Component ã§ã¯çæãããã«ã¹ã¿ã hooks ãæ¬¡ã®ããã«åŒã³åºããŠãµãŒããŒããè¿ã£ãŠããçµæãåãåã£ãŠãããŒã¿åºåãããŒãã£ã³ã°ç¶æ
ã®ãã§ãã¯ããšã©ãŒãã³ããªã³ã°çãè¡ããŸãã const { data , loading , error } = useAllPostsQuery (); Mutation ããŒã¿ã®æžã蟌ã¿ã¯ useMutation ã§è¡ããŸãã Query åæ§ã GraphQL Code Generator ã«ãã£ãŠçæãããã«ã¹ã¿ã hooks use***Mutation ã䜿ã£ãŠããŸãã cache ã®æŽæ° Mutation ãè€æ°ãšã³ãã£ãã£ã®æŽæ°ããšã³ãã£ãã£ã®æ°èŠäœæãŸãã¯åé€ã®å ŽåãApollo Client ã® cache ã¯èªåæŽæ°ããããMutation ã®çµæãèªåçã« render ãããŸããã ãã®ãããªå Žåã§ãã useMutation ã® update option ã䜿ãã°ã cache ãªããžã§ã¯ããåŒæ°ã«åãã颿°ãèšå®ã§ããã®ã§ããã®é¢æ°å
ã§çŽæ¥ cache ãæŽæ°ã§ããŸãã ãŸãã update ã®ä»£ããã« refetchQueries ã® option ã䜿ã£ãŠãä»»æã® Query ãå®è¡ããŠãã·ã³ãã«ã« cache ãæŽæ°ããããšãã§ããŸãã äœãããã®æ¹æ³ã ãš Network éä¿¡ã«ãããªãŒããŒããããçºçããŸãã ãã®ãªãŒããŒããããç ç²ã«ããŠã§ãããµãŒããŒããããŒã¿ååŸããã Query ããããããªå Žåã«ã¯ããã® refetchQueries ãæå¹ã§ãã local state ã®ç®¡ç ããããã¯ç¹å®ã® component ã®ç¶æ
管çã local state ã䜿ã£ãŠã©ã®ããã«ç®¡çããŠãããããã説æããŠãããŸãã @client ã䜿ã£ã Query Next.js ã®ãããžã§ã¯ãã§ãlocal state ã®ç®¡çã Apollo Client ã§è¡ãå Žåã®äŸãšããŠã¯ã次ã®éãã§ãã ã¹ããŒãïŒ # src / components / app / Home / apollo . schema . graphql type Home { currentPostId: Int! } extend type Query { home: Home } ã¯ãšãªïŒ # src / components / app / Home / apollo . query . graphql query HomeCurrentPostId { home @ client { currentPostId } } ãã£ãã·ã¥ã®åæå€ïŒ // src/components/app/Home/apollo.cache.ts export const cache = { __typename: 'Home' , currentPostId: 0 , ..., }; // src/apollo/cache.ts const caches = { ..., home: home . cache , }; export { ..., caches , }; // src/pages/_app.tsx export const cache = new InMemoryCache (); ... const client = new ApolloClient ({ link , cache: cache . restore ( initialState || {}), resolvers , connectToDevTools: true , }); cache . writeData ({ data: caches }); GraphQL ã¯ãšãªãšã¹ããŒããå®çŸ©ãããŠããã° GraphQL Code Generator ã use***Query ã®ã³ãŒããçæããèšå®ã«ããŠããŸãã ããŒã«ã«ããŒã¿ã®å Žåãã¯ãšãªã§ @client ãã£ã¬ã¯ãã£ããã€ããŠããŒã«ã«ããŒã¿ã§ããããšãæç€ºããŸãã @client ã䜿ã£ã Mutation local state ã®æŽæ°ã GraphQL ã® Mutation ãšããŠè¡ãå Žåã®äŸãšããŠã¯ã次ã®éãã§ãã ã¹ããŒãïŒ # src / components / app / Home / apollo . schema . graphql type UpdateCurrentPostId { currentPostId: Int! } extend type Mutation { updateCurrentPostId(id: Int!): UpdateCurrentPostId } ã¯ãšãªïŒ # src / components / app / Home / apollo . query . graphql mutation UpdateCurrentPostId ( $id : Int !) { updateCurrentPostId ( id : $id ) @ client { currentPostId @ client } } resolverïŒ // src/components/app/Home/apollo.cache.ts const updateCurrentPostId : MutationResolvers [ "updateCurrentPostId" ] = ( _ , args , { cache } ) => { cache . writeData ({ data: { home: { __typename: "Home" , currentPostId: args . id , }, }, }); return null ; }; export const Mutation = { updateCurrentPostId , }; Query åæ§ã« @client ãã£ã¬ã¯ãã£ããã€ããŠããŒã«ã«ããŒã¿ã§ããããšãæç€ºããŸãã å®éã® Mutation ã®åŠçèªäœã¯ resolver ã®äžã« cache.writeData() ã䜿ã£ãŠèšè¿°ããŸãã Mutation ã®åœåã¯ã åè©+åè©ã®åœ¢åŒã§å¯èœãªéãæå³ã®ããå
·äœçãªååãã€ãã ããšãæèããŠããŸãã Apollo ã䜿ã£ãéçºã䟿å©ã«ããŠãããããŒã« Apollo Client ã䜿ã£ãŠéçºããéã¯ãããŒã«ã«ã® Apollo cache ã®ç¶æ
ããã¯ãšãªã詊ãã«å®è¡ããããã®ããŒã«ãšããŠãGoogle Chrome ã®æ¡åŒµæ©èœ Apollo Client Developer Tools ãéåžžã«äŸ¿å©ã§ãã ãã¡ãã®æ¡åŒµæ©èœã Chrome ã«ã€ã³ã¹ããŒã«ãããšãApollo Client ã䜿ã£ãŠ GraphQL API ã«ã¢ã¯ã»ã¹ãããµã€ãã«é·ç§»ããç¶æ
ã§ Chrome Dev Tools ãéããš Apollo ã®ã¿ãã衚瀺ãããŸããããã§ã¯ãšãªã®å®è¡ããAPI 仿§ã®ç¢ºèªãããŒã«ã«ã® Apollo cache ã®ç¢ºèªçãè¡ãããšãã§ããŸãã GraphQL é¢é£ã®ãã¹ãã³ãŒãã«ã€ã㊠Apollo Client ã䜿ã£ã React Component ã®éçºã§ãQuery ããã³ Mutation å®è¡ã®ãã¹ãã宿œããã«ã¯ããã¹ããã¬ãŒã ã¯ãŒã¯ã® Jestãreact-testing-library ãšããããŠãApollo å
¬åŒã§ã玹ä»ãããŠãã MockedProvider ãçšããæ¹æ³ãäžè¬çããšæããŸãã ã¯ãšãªãšã¯ãšãªã«å¯Ÿããã¬ã¹ãã³ã¹ãçµã¿åãããã¢ãã¯ããŒã¿ãçšæããŠãããApolloProvider ã®ä»£ããã« MockedProvider ã§ãã¹ã察象㮠component ãã©ããããããšã§ãAPI ãµãŒããŒã Network ç°å¢ã«äŸåãããã¢ãã¯ã§æå®ããã¯ãšãªããªã¯ãšã¹ãããããšãã¢ãã¯ã§ããã«å¯Ÿå¿ããããã«çšæããã¬ã¹ãã³ã¹ããŒã¿ã確å®ã«ååŸã§ããä»çµã¿ãäœããŸãã ãã®ä»çµã¿ãš react-testing-library ã䜿ã£ãŠãcomponent ã§ render ããã UI äžã®æäœãããªã¬ãŒã«ããŠå®è¡ããããã¯ãšãªã®ãã¹ããè¡ãããšãã§ããŸãã Query ã ãã§ã¯ãªã Mutation ãã¢ãã¯ããããšãã§ããŠã䟿å©ãªããŒã«ã§ã¯ãããŸããããã¹ãã±ãŒã¹æ¯ã«ã¢ãã¯ããŒã¿ã¯æåã§äœæããªããã°ãªããªãç¹ãããªããªã骚ãæããäœæ¥ã§ãã å®éã«ã¢ããªã±ãŒã·ã§ã³ãåãããŠããã¹ã察象㮠component ã render ããQuery ã«æž¡ããã variables ãã¬ã¹ãã³ã¹ã®å€ã Console ã«åºåãããã©ãŠã¶ã® Dev Tools äžã§äžåäžåãªããžã§ã¯ããã³ããŒããŠããšãã£ã¿ã«è²Œãä»ããããããäœæ¥ãçºçããŸãã AutoMockedProvider ã®äœæ ããã§ããããããã¹ãäœæãã¹ããŒã倿Žã®åºŠã«ãæåã§ã¢ãã¯ããŒã¿ãçšæããªããŠããGraphQL ã¹ããŒãã§å®çŸ©ãããŠããåãèŠãŠãèªåã§ã¯ãšãªã«å¯Ÿããã¬ã¹ãã³ã¹ãã¢ãã¯ããŠããã AutoMockedProvider ãã ãã¡ãã®èšäº ãåèã«ããŠäœæããŸããã MockedProvider ã®ä»£ããã«ãAutoMockedProvider ãçšããŠãã¹ã察象㮠Component ãã©ããããããšã§ãMockedProvider ã䜿ã£ãŠãã¹ãããŠããå
容ãšåããã¹ãã宿œã§ããŸãã MockedProvider ã䜿ã£ãŠæ¯åã¢ãã¯ããŒã¿ãçšæãããã¹ãã宿œããããšã«ç²ããŠããæ¹ã¯æ¯éãã詊ããã ããã ïŒç޹ä»å
ã®èšäºã§ã¯ã graphql-tools ã® makeExecutableSchema() ã«æž¡ã schemaSDL ã json ãã¡ã€ã«ã§å®çŸ©ãããŠããŸããã graphql-tag ã®ã©ã€ãã©ãªã䜵çšããã°ãgraphql ãã¡ã€ã«ã§ãåæ§ã« schemaSDL ãšããŠé©çšããããšãå¯èœã§ãïŒ ãªãã¥ãŒã¢ã«ãæ¯ãè¿ã£ãŠ ä»åã®ãªãã¥ãŒã¢ã«ã§ã¯ãGraphQLãTypeScriptãReact ãã»ããã§æ¡çšããããšã«ãããããã³ãåŽã§ã¯ GraphQL Code Generator ã䜿ã£ãŠããããããçšæããŠããã GraphQL ã¹ããŒããããTypeScript ã®åã ãã§ã¯ãªããReact ã® Hooks 颿°ãŸã§çæããŠå©çšã§ããããšããéçºå¹çã®åäžã«éåžžã«åœ±é¿ãäžãããšæããŸãã GraphQL API ã®ã¯ã©ã€ã¢ã³ãã§ãã¢ããªã±ãŒã·ã§ã³å
šäœã®ç¶æ
管çãè¡ã Apollo Client ã® cache æ©æ§ã®äœ¿ãæ¹çãäœåŸãããŸã§ã«ãåŠç¿ã³ã¹ãã¯æ±ºããŠãŒãã§ã¯ãããŸããã§ããããTypeScript ãš GraphQL ã®åã·ã¹ãã ã®æ©æµããã«ã«åããNext.js ã®ã¬ãŒã«ã«ã®ã£ãããåå®å
šãªéçºç°å¢ãæã«å
¥ããããšãã§ããŸããã æã
ãéçºè
ã®äœéšã ãã§ã¯ãªããä»åŸã®ãããã¯ãå
šäœãžã®çç£æ§ã«ãè¯ã圱é¿ãåãŒããŠããããšç¢ºä¿¡ããŠããŸãã ãããã« ã¡ãã¬ãŒã§ã¯ãšã³ãžãã¢ã»ãã¶ã€ããŒãç©æ¥µåéããŠããŸãã ããã¯ãããžãŒã掻çšããŠå»çãã«ã¹ã±ã¢ã®æªæ¥ãã€ããããšããããã·ã§ã³ã«å
±æãã課é¡è§£æ±ºãè¡ãããæ¹ã¯æ¯éããå¿åãã ããã åéã®äžèЧ | æ ªåŒäŒç€Ÿã¡ãã¬ãŒ ã¡ãã¬ãŒã®æ¡çšæ
å ±ã¯ãã¡ãããã確èªãã ããã www.medley.jp