[{"data":1,"prerenderedAt":3444},["ShallowReactive",2],{"series-rtmp-manager-server-1":3},{"doc":4,"prev":1574,"next":1576},{"id":5,"title":6,"body":7,"category":1559,"createdAt":1561,"description":1562,"extension":1563,"index":185,"meta":1564,"navigation":365,"path":1565,"publish":365,"seo":1566,"series":1567,"seriesTitle":1568,"stem":1569,"tag":1570,"thumbnail":1573,"updatedAt":1574,"__hash__":1575},"series\u002Fseries\u002Frtmp-manager-server-1.md","配信中継サーバ開発1：ローカルのDockerとNginxを用いてGoproからRTMPで送信されたライブ配信映像を再生する",{"type":8,"value":9,"toc":1532},"minimark",[10,17,21,24,28,31,44,48,51,62,70,72,75,78,81,84,89,92,95,107,110,121,125,128,132,140,144,147,162,172,175,195,206,211,334,337,340,345,348,647,654,657,738,744,747,754,962,965,968,974,1377,1383,1386,1392,1395,1399,1403,1406,1417,1420,1423,1426,1438,1446,1452,1461,1464,1467,1470,1474,1480,1484,1491,1494,1497,1500,1503,1506,1528],[11,12,16],"div",{"className":13},[14,15],"alert","alert-info","\nこの記事はもともと[https:\u002F\u002Fjun-app.com\u002Farticles\u002Frtmp-docker-local](https:\u002F\u002Fjun-app.com\u002Farticles\u002Frtmp-docker-local) にて掲載していました。この記事に関連する内容をシリーズ化するため、このURLに移動しました。\n",[18,19,20],"p",{},"こんにちはjunです。今回の記事は一風変わって物理世界をちょっと扱います。いつもはPC内で行えることばかりやっていますが、今回はGoproをつかってタイトルの通りライブ映像を再生したいと思います。RTMPサーバーをローカルで作成し、そこに送信できるかのテストをしてみたかった感じです。",[18,22,23],{},"ローカルPCのDockerにNignxをインストールしてRTMPの設定をしてブラウザから見れるようにします。この記事ではまずなぜ、このようなことを始めたいのかという理由から述べます。実装内容をさっさと知りたい方は「RTMPサーバー実装」へ移動してください。",[25,26,27],"h2",{"id":27},"事の経緯",[18,29,30],{},"経緯としては私がyoutubeライブをやりたくなったからです笑。単純にyoutubeライブをやりたいだけならば、さっさとwebカメラからやればいいのですが、私はサイクリングが趣味でなのでその風景をライブしたいと思っていました。持っているアクションカメラがGoproであり、一応公式のスマホアプリを使用することでyoutubeライブをすることができるのですが以下の懸念がありました。",[32,33,34,38,41],"ul",{},[35,36,37],"li",{},"Gopro、スマホアプリなどカメラ側の不具合によるライブの中断",[35,39,40],{},"柔軟なカメラ切り替えを行える環境の準備",[35,42,43],{},"スマホからのyoutubeライブ配信は登録者が50人必要（１番の障壁です笑）",[45,46,47],"h3",{"id":47},"カメラ側によるライブの中断",[18,49,50],{},"Goproは簡単にライブ配信ができるのですが、以下の様なカメラ・スマホ側の要因によってライブが中断される可能性があります。",[32,52,53,56,59],{},[35,54,55],{},"Goproの熱暴走",[35,57,58],{},"トンネルなどでwi-fi・ネットワーク切断",[35,60,61],{},"カメラ、スマホの電池切れ",[18,63,64,65,69],{},"本当に中断されるかは ",[66,67,68],"strong",{},"未検証"," ですが可能性があるのでこれを避けるためには「Gopro側からライブの設定を制御するのでなく、Goproからは音声と映像を取得するのみで配信は別環境で行う」ことがベストな気がします。",[45,71,40],{"id":40},[18,73,74],{},"ライブ配信では映しっぱなしでもいいのですが、撮影禁止の箇所だったり環境によって映像のON\u002FOFFを切り替えたいと思っています。Goproアプリは確かにライブ配信は簡単なのですが、より細かい設定や制御は実装されていません。",[45,76,77],{"id":77},"登録者が50人必要",[18,79,80],{},"スマホ通じてライブ配信をする場合はどうやら50人が必要でしたが、RTMPやPCからであれば0人でもOKでした。",[18,82,83],{},"登録者的な問題だったり、より細かい制御を実装するために以下のような構成を考えた結果、まずは自前RTMPサーバーへの映像の送信と再生ができるかをテストしてみたかった次第です。最終的にはRTMPサーバーから映像を取得してyoutubeに流します。サーバーでは映像の表示制御やyoutubeのコメント読み上げなど配信に関係する機能を管理するものとします。",[85,86],"image-render",{":src":87,":width":88},"'rtmp-docker-local\u002Ffig.png'","'100%'",[25,90,91],{"id":91},"実装概要",[18,93,94],{},"この記事での目標は",[96,97,98,101,104],"ol",{},[35,99,100],{},"Goproからの映像をアプリを通じてコンテナ内のWebサーバーにRTMP通信で映像を送信。",[35,102,103],{},"映像をドキュメントルートへ配置。",[35,105,106],{},"webブラウザからアクセスして映像を見れうようにする。",[18,108,109],{},"まずはRTMPサーバーの実装を行います。使用しているOSはmacOs Catalina 10.15.5でdockerは3.5.2です。実装の手順としては",[96,111,112,115,118],{},[35,113,114],{},"Dockerを用いてRTMPとwebが可能なNginxを構築する。",[35,116,117],{},"RTMPの設定をする。",[35,119,120],{},"web側でHLSにて映像をvideoタグを用いて再生する。",[45,122,124],{"id":123},"rtmpとは","RTMPとは？",[18,126,127],{},"ここまで出ているRTMPとはReal Time Messaging ProtocolのことでTCP上のプロトコルで映像、音声、データを細かいフラグメントに分けてストリーミング送信ができます。今回のようなリアルタイムに映像を撮影してサーバーに送信する際にも利用されます。",[45,129,131],{"id":130},"なぜnginx","なぜNginx？",[18,133,134,135,139],{},"NginxではRTMPモジュールというのがあり、インストールしてconfファイルに少し記述するだけで1935ポートに",[136,137,138],"code",{},"rtmp:\u002F\u002Fexample.com\u002Flive","のようなURLでサーバーに送信できます。",[25,141,143],{"id":142},"rtmpサーバー実装","RTMPサーバー実装",[45,145,146],{"id":146},"docker-composeの作成",[18,148,149,150,153,154,157,158,161],{},"では早速やっていきましょう。",[136,151,152],{},"rtmptest","みたいな適当なディレクトリを作成して、",[136,155,156],{},"Dockerfile","と",[136,159,160],{},"docker-compose.yml","ファイルを作成します。また、webとconfファイルを格納してボリュームしておくディレクトリも作成します。",[163,164,169],"pre",{"className":165,"code":167,"language":168},[166],"language-text","mkdir rtmptest\ncd rtmptest\n\ntouch Dockerfile docker-compose.yml\nmkdir html conf\n","text",[136,170,167],{"__ignoreMap":171},"",[18,173,174],{},"Dockerfileは以下のようにしておきます。",[163,176,179],{"className":177,"code":178,"language":156,"meta":171,"style":171},"language-Dockerfile shiki shiki-themes material-theme-ocean","FROM tiangolo\u002Fnginx-rtmp\nVOLUME [\"\u002Fusr\u002Fshare\u002Fnginx\u002Fhtml\",\"\u002Fetc\u002Fnginx\"]\n",[136,180,181,189],{"__ignoreMap":171},[182,183,186],"span",{"class":184,"line":185},"line",1,[182,187,188],{},"FROM tiangolo\u002Fnginx-rtmp\n",[182,190,192],{"class":184,"line":191},2,[182,193,194],{},"VOLUME [\"\u002Fusr\u002Fshare\u002Fnginx\u002Fhtml\",\"\u002Fetc\u002Fnginx\"]\n",[18,196,197,198,205],{},"今回は",[199,200,204],"a",{"href":201,"rel":202},"https:\u002F\u002Fhub.docker.com\u002Fr\u002Ftiangolo\u002Fnginx-rtmp\u002F",[203],"nofollow","rtmpの設定があるnginxのイメージ","を利用します。",[18,207,208,210],{},[136,209,160],{},"は以下の通りです。",[163,212,217],{"className":213,"code":214,"filename":215,"language":216,"meta":171,"style":171},"language-yaml shiki shiki-themes material-theme-ocean","version: '2'\nservices:\n  app:\n    build: .\n    ports:\n      - \"8085:80\"\n      - \"1935:1935\"\n    volumes:\n      -  .\u002Fhtml:\u002Fusr\u002Fshare\u002Fnginx\u002Fhtml\n      -  .\u002Fconf\u002Fnginx.conf:\u002Fetc\u002Fnginx\u002Fnginx.conf\n      -  .\u002Fconf\u002Fdefault.conf:\u002Fetc\u002Fnginx\u002Fconf.d\u002Fdefault.conf\n","docke-compose.yml","yaml",[136,218,219,239,247,255,267,275,290,302,310,318,326],{"__ignoreMap":171},[182,220,221,225,229,232,236],{"class":184,"line":185},[182,222,224],{"class":223},"s-wAU","version",[182,226,228],{"class":227},"sAklC",":",[182,230,231],{"class":227}," '",[182,233,235],{"class":234},"sfyAc","2",[182,237,238],{"class":227},"'\n",[182,240,241,244],{"class":184,"line":191},[182,242,243],{"class":223},"services",[182,245,246],{"class":227},":\n",[182,248,250,253],{"class":184,"line":249},3,[182,251,252],{"class":223},"  app",[182,254,246],{"class":227},[182,256,258,261,263],{"class":184,"line":257},4,[182,259,260],{"class":223},"    build",[182,262,228],{"class":227},[182,264,266],{"class":265},"sx098"," .\n",[182,268,270,273],{"class":184,"line":269},5,[182,271,272],{"class":223},"    ports",[182,274,246],{"class":227},[182,276,278,281,284,287],{"class":184,"line":277},6,[182,279,280],{"class":227},"      -",[182,282,283],{"class":227}," \"",[182,285,286],{"class":234},"8085:80",[182,288,289],{"class":227},"\"\n",[182,291,293,295,297,300],{"class":184,"line":292},7,[182,294,280],{"class":227},[182,296,283],{"class":227},[182,298,299],{"class":234},"1935:1935",[182,301,289],{"class":227},[182,303,305,308],{"class":184,"line":304},8,[182,306,307],{"class":223},"    volumes",[182,309,246],{"class":227},[182,311,313,315],{"class":184,"line":312},9,[182,314,280],{"class":227},[182,316,317],{"class":234},"  .\u002Fhtml:\u002Fusr\u002Fshare\u002Fnginx\u002Fhtml\n",[182,319,321,323],{"class":184,"line":320},10,[182,322,280],{"class":227},[182,324,325],{"class":234},"  .\u002Fconf\u002Fnginx.conf:\u002Fetc\u002Fnginx\u002Fnginx.conf\n",[182,327,329,331],{"class":184,"line":328},11,[182,330,280],{"class":227},[182,332,333],{"class":234},"  .\u002Fconf\u002Fdefault.conf:\u002Fetc\u002Fnginx\u002Fconf.d\u002Fdefault.conf\n",[45,335,336],{"id":336},"confファイル調整",[18,338,339],{},"nginxのconfファイルを調整してRTMPとwebアクセスができるようにします。",[341,342,344],"h4",{"id":343},"rtmp","RTMP",[18,346,347],{},"RTMPはnginx.confというapacheで言うhttp.confに以下のように記述します。",[163,349,354],{"className":350,"code":351,"filename":352,"language":353,"meta":171,"style":171},"language-conf shiki shiki-themes material-theme-ocean","worker_processes  auto;\n\nerror_log  \u002Fvar\u002Flog\u002Fnginx\u002Ferror.log notice;\npid        \u002Fvar\u002Frun\u002Fnginx.pid;\n\n\nevents {\n    worker_connections  1024;\n}\n\nrtmp_auto_push on;\n\nrtmp {\n    server {\n        listen 1935;\n        listen [::]:1935 ipv6only=on;\n        access_log \u002Fvar\u002Flog\u002Frtmp_access.log;\n        chunk_size 4096;\n        timeout 10s;\n\n        application live {\n            live on;\n\n            # HLSの記述欄\n            hls on;\n            # ここに映像ファイルが配置される\n            hls_path \u002Fusr\u002Fshare\u002Fnginx\u002Fhtml\u002Fhls;\n            hls_fragment 10s;\n        }\n    }\n}\n\n\n\nhttp {\n    include       \u002Fetc\u002Fnginx\u002Fmime.types;\n    default_type  application\u002Foctet-stream;\n\n    log_format  main  '$remote_addr - $remote_user [$time_local] \"$request\" '\n                      '$status $body_bytes_sent \"$http_referer\" '\n                      '\"$http_user_agent\" \"$http_x_forwarded_for\"';\n\n    access_log  \u002Fvar\u002Flog\u002Fnginx\u002Faccess.log  main;\n\n    sendfile        on;\n    #tcp_nopush     on;\n\n    keepalive_timeout  65;\n\n    #gzip  on;\n\n    include \u002Fetc\u002Fnginx\u002Fconf.d\u002F*.conf;\n}\n","conf\u002Fnginx.conf","conf",[136,355,356,361,367,372,377,381,385,390,395,400,404,409,414,420,426,432,438,444,450,456,461,467,473,478,484,490,496,502,508,514,520,525,530,535,540,546,552,558,563,569,575,581,586,592,597,603,609,614,620,625,631,636,642],{"__ignoreMap":171},[182,357,358],{"class":184,"line":185},[182,359,360],{},"worker_processes  auto;\n",[182,362,363],{"class":184,"line":191},[182,364,366],{"emptyLinePlaceholder":365},true,"\n",[182,368,369],{"class":184,"line":249},[182,370,371],{},"error_log  \u002Fvar\u002Flog\u002Fnginx\u002Ferror.log notice;\n",[182,373,374],{"class":184,"line":257},[182,375,376],{},"pid        \u002Fvar\u002Frun\u002Fnginx.pid;\n",[182,378,379],{"class":184,"line":269},[182,380,366],{"emptyLinePlaceholder":365},[182,382,383],{"class":184,"line":277},[182,384,366],{"emptyLinePlaceholder":365},[182,386,387],{"class":184,"line":292},[182,388,389],{},"events {\n",[182,391,392],{"class":184,"line":304},[182,393,394],{},"    worker_connections  1024;\n",[182,396,397],{"class":184,"line":312},[182,398,399],{},"}\n",[182,401,402],{"class":184,"line":320},[182,403,366],{"emptyLinePlaceholder":365},[182,405,406],{"class":184,"line":328},[182,407,408],{},"rtmp_auto_push on;\n",[182,410,412],{"class":184,"line":411},12,[182,413,366],{"emptyLinePlaceholder":365},[182,415,417],{"class":184,"line":416},13,[182,418,419],{},"rtmp {\n",[182,421,423],{"class":184,"line":422},14,[182,424,425],{},"    server {\n",[182,427,429],{"class":184,"line":428},15,[182,430,431],{},"        listen 1935;\n",[182,433,435],{"class":184,"line":434},16,[182,436,437],{},"        listen [::]:1935 ipv6only=on;\n",[182,439,441],{"class":184,"line":440},17,[182,442,443],{},"        access_log \u002Fvar\u002Flog\u002Frtmp_access.log;\n",[182,445,447],{"class":184,"line":446},18,[182,448,449],{},"        chunk_size 4096;\n",[182,451,453],{"class":184,"line":452},19,[182,454,455],{},"        timeout 10s;\n",[182,457,459],{"class":184,"line":458},20,[182,460,366],{"emptyLinePlaceholder":365},[182,462,464],{"class":184,"line":463},21,[182,465,466],{},"        application live {\n",[182,468,470],{"class":184,"line":469},22,[182,471,472],{},"            live on;\n",[182,474,476],{"class":184,"line":475},23,[182,477,366],{"emptyLinePlaceholder":365},[182,479,481],{"class":184,"line":480},24,[182,482,483],{},"            # HLSの記述欄\n",[182,485,487],{"class":184,"line":486},25,[182,488,489],{},"            hls on;\n",[182,491,493],{"class":184,"line":492},26,[182,494,495],{},"            # ここに映像ファイルが配置される\n",[182,497,499],{"class":184,"line":498},27,[182,500,501],{},"            hls_path \u002Fusr\u002Fshare\u002Fnginx\u002Fhtml\u002Fhls;\n",[182,503,505],{"class":184,"line":504},28,[182,506,507],{},"            hls_fragment 10s;\n",[182,509,511],{"class":184,"line":510},29,[182,512,513],{},"        }\n",[182,515,517],{"class":184,"line":516},30,[182,518,519],{},"    }\n",[182,521,523],{"class":184,"line":522},31,[182,524,399],{},[182,526,528],{"class":184,"line":527},32,[182,529,366],{"emptyLinePlaceholder":365},[182,531,533],{"class":184,"line":532},33,[182,534,366],{"emptyLinePlaceholder":365},[182,536,538],{"class":184,"line":537},34,[182,539,366],{"emptyLinePlaceholder":365},[182,541,543],{"class":184,"line":542},35,[182,544,545],{},"http {\n",[182,547,549],{"class":184,"line":548},36,[182,550,551],{},"    include       \u002Fetc\u002Fnginx\u002Fmime.types;\n",[182,553,555],{"class":184,"line":554},37,[182,556,557],{},"    default_type  application\u002Foctet-stream;\n",[182,559,561],{"class":184,"line":560},38,[182,562,366],{"emptyLinePlaceholder":365},[182,564,566],{"class":184,"line":565},39,[182,567,568],{},"    log_format  main  '$remote_addr - $remote_user [$time_local] \"$request\" '\n",[182,570,572],{"class":184,"line":571},40,[182,573,574],{},"                      '$status $body_bytes_sent \"$http_referer\" '\n",[182,576,578],{"class":184,"line":577},41,[182,579,580],{},"                      '\"$http_user_agent\" \"$http_x_forwarded_for\"';\n",[182,582,584],{"class":184,"line":583},42,[182,585,366],{"emptyLinePlaceholder":365},[182,587,589],{"class":184,"line":588},43,[182,590,591],{},"    access_log  \u002Fvar\u002Flog\u002Fnginx\u002Faccess.log  main;\n",[182,593,595],{"class":184,"line":594},44,[182,596,366],{"emptyLinePlaceholder":365},[182,598,600],{"class":184,"line":599},45,[182,601,602],{},"    sendfile        on;\n",[182,604,606],{"class":184,"line":605},46,[182,607,608],{},"    #tcp_nopush     on;\n",[182,610,612],{"class":184,"line":611},47,[182,613,366],{"emptyLinePlaceholder":365},[182,615,617],{"class":184,"line":616},48,[182,618,619],{},"    keepalive_timeout  65;\n",[182,621,623],{"class":184,"line":622},49,[182,624,366],{"emptyLinePlaceholder":365},[182,626,628],{"class":184,"line":627},50,[182,629,630],{},"    #gzip  on;\n",[182,632,634],{"class":184,"line":633},51,[182,635,366],{"emptyLinePlaceholder":365},[182,637,639],{"class":184,"line":638},52,[182,640,641],{},"    include \u002Fetc\u002Fnginx\u002Fconf.d\u002F*.conf;\n",[182,643,645],{"class":184,"line":644},53,[182,646,399],{},[18,648,649,650,653],{},"このファイルはコンテナ内の",[136,651,652],{},"nginx.conf","にボリュームします。tiangolo\u002Fnginx-rtmpのイメージで生成されるnginx.confはRTMPのために最低限の記述しかないので上記のようにします。",[18,655,656],{},"RTMPでは以下の設定です。",[163,658,660],{"className":350,"code":659,"language":353,"meta":171,"style":171},"rtmp {\n    server {\n        listen 1935;\n        listen [::]:1935 ipv6only=on;\n        access_log \u002Fvar\u002Flog\u002Frtmp_access.log;\n        chunk_size 4096;\n        timeout 10s;\n\n        application live {\n            live on;\n\n            # HLSの記述欄\n            hls on;\n            # ここに映像ファイルが配置される\n            hls_path \u002Fusr\u002Fshare\u002Fnginx\u002Fhtml\u002Fhls;\n            hls_fragment 10s;\n        }\n    }\n}\n",[136,661,662,666,670,674,678,682,686,690,694,698,702,706,710,714,718,722,726,730,734],{"__ignoreMap":171},[182,663,664],{"class":184,"line":185},[182,665,419],{},[182,667,668],{"class":184,"line":191},[182,669,425],{},[182,671,672],{"class":184,"line":249},[182,673,431],{},[182,675,676],{"class":184,"line":257},[182,677,437],{},[182,679,680],{"class":184,"line":269},[182,681,443],{},[182,683,684],{"class":184,"line":277},[182,685,449],{},[182,687,688],{"class":184,"line":292},[182,689,455],{},[182,691,692],{"class":184,"line":304},[182,693,366],{"emptyLinePlaceholder":365},[182,695,696],{"class":184,"line":312},[182,697,466],{},[182,699,700],{"class":184,"line":320},[182,701,472],{},[182,703,704],{"class":184,"line":328},[182,705,366],{"emptyLinePlaceholder":365},[182,707,708],{"class":184,"line":411},[182,709,483],{},[182,711,712],{"class":184,"line":416},[182,713,489],{},[182,715,716],{"class":184,"line":422},[182,717,495],{},[182,719,720],{"class":184,"line":428},[182,721,501],{},[182,723,724],{"class":184,"line":434},[182,725,507],{},[182,727,728],{"class":184,"line":440},[182,729,513],{},[182,731,732],{"class":184,"line":446},[182,733,519],{},[182,735,736],{"class":184,"line":452},[182,737,399],{},[18,739,740,741,743],{},"ここでは1935ポートでRTMPをリッスンし、また",[136,742,138],{},"というURLでアップロードできるようにしています。",[341,745,746],{"id":746},"web",[18,748,749,750,753],{},"webは以下のようにしておきます。ドキュメントルートは",[136,751,752],{},"\u002Fusr\u002Fshare\u002Fnginx\u002Fhtml","です。",[163,755,757],{"className":350,"code":756,"filename":352,"language":353,"meta":171,"style":171},"server {\n    listen       80;\n    listen  [::]:80;\n    server_name  localhost;\n\n    #access_log  \u002Fvar\u002Flog\u002Fnginx\u002Fhost.access.log  main;\n\n    location \u002F {\n        root   \u002Fusr\u002Fshare\u002Fnginx\u002Fhtml;\n        index  index.html index.htm;\n    }\n\n    #error_page  404              \u002F404.html;\n\n    # redirect server error pages to the static page \u002F50x.html\n    #\n    error_page   500 502 503 504  \u002F50x.html;\n    location = \u002F50x.html {\n        root   \u002Fusr\u002Fshare\u002Fnginx\u002Fhtml;\n    }\n\n    # proxy the PHP scripts to Apache listening on 127.0.0.1:80\n    #\n    #location ~ \\.php$ {\n    #    proxy_pass   http:\u002F\u002F127.0.0.1;\n    #}\n\n    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000\n    #\n    #location ~ \\.php$ {\n    #    root           html;\n    #    fastcgi_pass   127.0.0.1:9000;\n    #    fastcgi_index  index.php;\n    #    fastcgi_param  SCRIPT_FILENAME  \u002Fscripts$fastcgi_script_name;\n    #    include        fastcgi_params;\n    #}\n\n    # deny access to .htaccess files, if Apache's document root\n    # concurs with nginx's one\n    #\n    #location ~ \u002F\\.ht {\n    #    deny  all;\n    #}\n}\n",[136,758,759,764,769,774,779,783,788,792,797,802,807,811,815,820,824,829,834,839,844,848,852,856,861,865,870,875,880,884,889,893,897,902,907,912,917,922,926,930,935,940,944,949,954,958],{"__ignoreMap":171},[182,760,761],{"class":184,"line":185},[182,762,763],{},"server {\n",[182,765,766],{"class":184,"line":191},[182,767,768],{},"    listen       80;\n",[182,770,771],{"class":184,"line":249},[182,772,773],{},"    listen  [::]:80;\n",[182,775,776],{"class":184,"line":257},[182,777,778],{},"    server_name  localhost;\n",[182,780,781],{"class":184,"line":269},[182,782,366],{"emptyLinePlaceholder":365},[182,784,785],{"class":184,"line":277},[182,786,787],{},"    #access_log  \u002Fvar\u002Flog\u002Fnginx\u002Fhost.access.log  main;\n",[182,789,790],{"class":184,"line":292},[182,791,366],{"emptyLinePlaceholder":365},[182,793,794],{"class":184,"line":304},[182,795,796],{},"    location \u002F {\n",[182,798,799],{"class":184,"line":312},[182,800,801],{},"        root   \u002Fusr\u002Fshare\u002Fnginx\u002Fhtml;\n",[182,803,804],{"class":184,"line":320},[182,805,806],{},"        index  index.html index.htm;\n",[182,808,809],{"class":184,"line":328},[182,810,519],{},[182,812,813],{"class":184,"line":411},[182,814,366],{"emptyLinePlaceholder":365},[182,816,817],{"class":184,"line":416},[182,818,819],{},"    #error_page  404              \u002F404.html;\n",[182,821,822],{"class":184,"line":422},[182,823,366],{"emptyLinePlaceholder":365},[182,825,826],{"class":184,"line":428},[182,827,828],{},"    # redirect server error pages to the static page \u002F50x.html\n",[182,830,831],{"class":184,"line":434},[182,832,833],{},"    #\n",[182,835,836],{"class":184,"line":440},[182,837,838],{},"    error_page   500 502 503 504  \u002F50x.html;\n",[182,840,841],{"class":184,"line":446},[182,842,843],{},"    location = \u002F50x.html {\n",[182,845,846],{"class":184,"line":452},[182,847,801],{},[182,849,850],{"class":184,"line":458},[182,851,519],{},[182,853,854],{"class":184,"line":463},[182,855,366],{"emptyLinePlaceholder":365},[182,857,858],{"class":184,"line":469},[182,859,860],{},"    # proxy the PHP scripts to Apache listening on 127.0.0.1:80\n",[182,862,863],{"class":184,"line":475},[182,864,833],{},[182,866,867],{"class":184,"line":480},[182,868,869],{},"    #location ~ \\.php$ {\n",[182,871,872],{"class":184,"line":486},[182,873,874],{},"    #    proxy_pass   http:\u002F\u002F127.0.0.1;\n",[182,876,877],{"class":184,"line":492},[182,878,879],{},"    #}\n",[182,881,882],{"class":184,"line":498},[182,883,366],{"emptyLinePlaceholder":365},[182,885,886],{"class":184,"line":504},[182,887,888],{},"    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000\n",[182,890,891],{"class":184,"line":510},[182,892,833],{},[182,894,895],{"class":184,"line":516},[182,896,869],{},[182,898,899],{"class":184,"line":522},[182,900,901],{},"    #    root           html;\n",[182,903,904],{"class":184,"line":527},[182,905,906],{},"    #    fastcgi_pass   127.0.0.1:9000;\n",[182,908,909],{"class":184,"line":532},[182,910,911],{},"    #    fastcgi_index  index.php;\n",[182,913,914],{"class":184,"line":537},[182,915,916],{},"    #    fastcgi_param  SCRIPT_FILENAME  \u002Fscripts$fastcgi_script_name;\n",[182,918,919],{"class":184,"line":542},[182,920,921],{},"    #    include        fastcgi_params;\n",[182,923,924],{"class":184,"line":548},[182,925,879],{},[182,927,928],{"class":184,"line":554},[182,929,366],{"emptyLinePlaceholder":365},[182,931,932],{"class":184,"line":560},[182,933,934],{},"    # deny access to .htaccess files, if Apache's document root\n",[182,936,937],{"class":184,"line":565},[182,938,939],{},"    # concurs with nginx's one\n",[182,941,942],{"class":184,"line":571},[182,943,833],{},[182,945,946],{"class":184,"line":577},[182,947,948],{},"    #location ~ \u002F\\.ht {\n",[182,950,951],{"class":184,"line":583},[182,952,953],{},"    #    deny  all;\n",[182,955,956],{"class":184,"line":588},[182,957,879],{},[182,959,960],{"class":184,"line":594},[182,961,399],{},[18,963,964],{},"RTMPを含むNginxの設定はこれでOKです。",[45,966,967],{"id":967},"web側実装",[18,969,970,973],{},[136,971,972],{},"html\u002Ftest.html","として以下のように作成します。",[163,975,980],{"className":976,"code":977,"filename":978,"language":979,"meta":171,"style":171},"language-html shiki shiki-themes material-theme-ocean","\u003C!DOCTYPE html>\n\u003Chtml>\n\n\u003Chead>\n  \u003Cmeta charset=\"utf-8\">\n  \u003Ctitle>MediaElement\u003C\u002Ftitle>\n  \u003C!-- MediaElement style -->\n  \u003Clink rel=\"stylesheet\" href=\"\u002F\u002Fcdnjs.cloudflare.com\u002Fajax\u002Flibs\u002Fmediaelement\u002F4.2.9\u002Fmediaelementplayer.css\" \u002F>\n\u003C\u002Fhead>\n\n\u003Cbody>\n  \u003C!-- MediaElement -->\n  \u003Cscript src=\"\u002F\u002Fcdnjs.cloudflare.com\u002Fajax\u002Flibs\u002Fmediaelement\u002F4.2.9\u002Fmediaelement-and-player.js\">\u003C\u002Fscript>\n\n  \u003Cvideo id=\"player\" width=\"640\" height=\"360\">\n\u003C\u002Fbody>\n\u003Cscript type=\"text\u002Fjavascript\">\n\n      var player = new MediaElementPlayer('player', {\n        success: function(mediaElement, originalNode) {\n          console.log(\"Player initialised\");\n        }\n      });\n        player.setSrc(\"hls\u002F.m3u8\");\n\u003C\u002Fscript>\n\n\u003C\u002Fhtml>\n","test.html","html",[136,981,982,997,1006,1010,1019,1043,1064,1070,1104,1112,1116,1125,1130,1156,1160,1205,1213,1233,1237,1270,1296,1321,1325,1334,1357,1365,1369],{"__ignoreMap":171},[182,983,984,987,990,994],{"class":184,"line":185},[182,985,986],{"class":227},"\u003C!",[182,988,989],{"class":223},"DOCTYPE",[182,991,993],{"class":992},"sJ14y"," html",[182,995,996],{"class":227},">\n",[182,998,999,1002,1004],{"class":184,"line":191},[182,1000,1001],{"class":227},"\u003C",[182,1003,979],{"class":223},[182,1005,996],{"class":227},[182,1007,1008],{"class":184,"line":249},[182,1009,366],{"emptyLinePlaceholder":365},[182,1011,1012,1014,1017],{"class":184,"line":257},[182,1013,1001],{"class":227},[182,1015,1016],{"class":223},"head",[182,1018,996],{"class":227},[182,1020,1021,1024,1027,1030,1033,1036,1039,1041],{"class":184,"line":269},[182,1022,1023],{"class":227},"  \u003C",[182,1025,1026],{"class":223},"meta",[182,1028,1029],{"class":992}," charset",[182,1031,1032],{"class":227},"=",[182,1034,1035],{"class":227},"\"",[182,1037,1038],{"class":234},"utf-8",[182,1040,1035],{"class":227},[182,1042,996],{"class":227},[182,1044,1045,1047,1050,1053,1057,1060,1062],{"class":184,"line":277},[182,1046,1023],{"class":227},[182,1048,1049],{"class":223},"title",[182,1051,1052],{"class":227},">",[182,1054,1056],{"class":1055},"s0W1g","MediaElement",[182,1058,1059],{"class":227},"\u003C\u002F",[182,1061,1049],{"class":223},[182,1063,996],{"class":227},[182,1065,1066],{"class":184,"line":292},[182,1067,1069],{"class":1068},"sC9rS","  \u003C!-- MediaElement style -->\n",[182,1071,1072,1074,1077,1080,1082,1084,1087,1089,1092,1094,1096,1099,1101],{"class":184,"line":304},[182,1073,1023],{"class":227},[182,1075,1076],{"class":223},"link",[182,1078,1079],{"class":992}," rel",[182,1081,1032],{"class":227},[182,1083,1035],{"class":227},[182,1085,1086],{"class":234},"stylesheet",[182,1088,1035],{"class":227},[182,1090,1091],{"class":992}," href",[182,1093,1032],{"class":227},[182,1095,1035],{"class":227},[182,1097,1098],{"class":234},"\u002F\u002Fcdnjs.cloudflare.com\u002Fajax\u002Flibs\u002Fmediaelement\u002F4.2.9\u002Fmediaelementplayer.css",[182,1100,1035],{"class":227},[182,1102,1103],{"class":227}," \u002F>\n",[182,1105,1106,1108,1110],{"class":184,"line":312},[182,1107,1059],{"class":227},[182,1109,1016],{"class":223},[182,1111,996],{"class":227},[182,1113,1114],{"class":184,"line":320},[182,1115,366],{"emptyLinePlaceholder":365},[182,1117,1118,1120,1123],{"class":184,"line":328},[182,1119,1001],{"class":227},[182,1121,1122],{"class":223},"body",[182,1124,996],{"class":227},[182,1126,1127],{"class":184,"line":411},[182,1128,1129],{"class":1068},"  \u003C!-- MediaElement -->\n",[182,1131,1132,1134,1137,1140,1142,1144,1147,1149,1152,1154],{"class":184,"line":416},[182,1133,1023],{"class":227},[182,1135,1136],{"class":223},"script",[182,1138,1139],{"class":992}," src",[182,1141,1032],{"class":227},[182,1143,1035],{"class":227},[182,1145,1146],{"class":234},"\u002F\u002Fcdnjs.cloudflare.com\u002Fajax\u002Flibs\u002Fmediaelement\u002F4.2.9\u002Fmediaelement-and-player.js",[182,1148,1035],{"class":227},[182,1150,1151],{"class":227},">\u003C\u002F",[182,1153,1136],{"class":223},[182,1155,996],{"class":227},[182,1157,1158],{"class":184,"line":422},[182,1159,366],{"emptyLinePlaceholder":365},[182,1161,1162,1164,1167,1170,1172,1174,1177,1179,1182,1184,1186,1189,1191,1194,1196,1198,1201,1203],{"class":184,"line":428},[182,1163,1023],{"class":227},[182,1165,1166],{"class":223},"video",[182,1168,1169],{"class":992}," id",[182,1171,1032],{"class":227},[182,1173,1035],{"class":227},[182,1175,1176],{"class":234},"player",[182,1178,1035],{"class":227},[182,1180,1181],{"class":992}," width",[182,1183,1032],{"class":227},[182,1185,1035],{"class":227},[182,1187,1188],{"class":234},"640",[182,1190,1035],{"class":227},[182,1192,1193],{"class":992}," height",[182,1195,1032],{"class":227},[182,1197,1035],{"class":227},[182,1199,1200],{"class":234},"360",[182,1202,1035],{"class":227},[182,1204,996],{"class":227},[182,1206,1207,1209,1211],{"class":184,"line":434},[182,1208,1059],{"class":227},[182,1210,1122],{"class":223},[182,1212,996],{"class":227},[182,1214,1215,1217,1219,1222,1224,1226,1229,1231],{"class":184,"line":440},[182,1216,1001],{"class":227},[182,1218,1136],{"class":223},[182,1220,1221],{"class":992}," type",[182,1223,1032],{"class":227},[182,1225,1035],{"class":227},[182,1227,1228],{"class":234},"text\u002Fjavascript",[182,1230,1035],{"class":227},[182,1232,996],{"class":227},[182,1234,1235],{"class":184,"line":446},[182,1236,366],{"emptyLinePlaceholder":365},[182,1238,1239,1242,1245,1247,1250,1254,1257,1260,1262,1264,1267],{"class":184,"line":452},[182,1240,1241],{"class":992},"      var",[182,1243,1244],{"class":1055}," player ",[182,1246,1032],{"class":227},[182,1248,1249],{"class":227}," new",[182,1251,1253],{"class":1252},"sdLwU"," MediaElementPlayer",[182,1255,1256],{"class":1055},"(",[182,1258,1259],{"class":227},"'",[182,1261,1176],{"class":234},[182,1263,1259],{"class":227},[182,1265,1266],{"class":227},",",[182,1268,1269],{"class":227}," {\n",[182,1271,1272,1275,1277,1280,1282,1286,1288,1291,1294],{"class":184,"line":458},[182,1273,1274],{"class":1252},"        success",[182,1276,228],{"class":227},[182,1278,1279],{"class":992}," function",[182,1281,1256],{"class":227},[182,1283,1285],{"class":1284},"s7ZW3","mediaElement",[182,1287,1266],{"class":227},[182,1289,1290],{"class":1284}," originalNode",[182,1292,1293],{"class":227},")",[182,1295,1269],{"class":227},[182,1297,1298,1301,1304,1307,1309,1311,1314,1316,1318],{"class":184,"line":463},[182,1299,1300],{"class":1055},"          console",[182,1302,1303],{"class":227},".",[182,1305,1306],{"class":1252},"log",[182,1308,1256],{"class":223},[182,1310,1035],{"class":227},[182,1312,1313],{"class":234},"Player initialised",[182,1315,1035],{"class":227},[182,1317,1293],{"class":223},[182,1319,1320],{"class":227},";\n",[182,1322,1323],{"class":184,"line":469},[182,1324,513],{"class":227},[182,1326,1327,1330,1332],{"class":184,"line":475},[182,1328,1329],{"class":227},"      }",[182,1331,1293],{"class":1055},[182,1333,1320],{"class":227},[182,1335,1336,1339,1341,1344,1346,1348,1351,1353,1355],{"class":184,"line":480},[182,1337,1338],{"class":1055},"        player",[182,1340,1303],{"class":227},[182,1342,1343],{"class":1252},"setSrc",[182,1345,1256],{"class":1055},[182,1347,1035],{"class":227},[182,1349,1350],{"class":234},"hls\u002F.m3u8",[182,1352,1035],{"class":227},[182,1354,1293],{"class":1055},[182,1356,1320],{"class":227},[182,1358,1359,1361,1363],{"class":184,"line":486},[182,1360,1059],{"class":227},[182,1362,1136],{"class":223},[182,1364,996],{"class":227},[182,1366,1367],{"class":184,"line":492},[182,1368,366],{"emptyLinePlaceholder":365},[182,1370,1371,1373,1375],{"class":184,"line":498},[182,1372,1059],{"class":227},[182,1374,979],{"class":223},[182,1376,996],{"class":227},[18,1378,1379,1380,1382],{},"hlsが再生できるようにjsの再生ライブラリを用意します。",[136,1381,1350],{},"については後述します。",[45,1384,1385],{"id":1385},"dokcer起動",[163,1387,1390],{"className":1388,"code":1389,"language":168},[166],"docker-compose up -d\n",[136,1391,1389],{"__ignoreMap":171},[18,1393,1394],{},"を用いてdockerイメージを起動しましょう。",[25,1396,1398],{"id":1397},"配信テスト","配信テスト！",[45,1400,1402],{"id":1401},"macのipを確認","MacのIPを確認",[18,1404,1405],{},"今回のはMac上にRTMPサーバを立てたのでMacのIPを確認しておきます。「システム環境設定」から「ネットワーク」を選択し、Wi-FiからIPアドレスを確認します。このPCは「192.168.0.3」なので",[18,1407,1408,1409,1412,1413,1416],{},"RTMPは",[136,1410,1411],{},"rtmp:\u002F\u002F192.168.0.3\u002Flive","、webは",[136,1414,1415],{},"http:\u002F\u002F192.168.0.3\u002Findex.html","でアクセスできます。",[85,1418],{":src":1419,":width":88},"'rtmp-docker-local\u002Fmac.png'",[45,1421,1422],{"id":1422},"gopro側の設定",[18,1424,1425],{},"Goproとアプリのテザリングなどは省略します。",[18,1427,1428,1433],{},[199,1429,1432],{"href":1430,"rel":1431},"https:\u002F\u002Fapps.apple.com\u002Fjp\u002Fapp\u002Fgopro-quik-%E5%8B%95%E7%94%BB-%E5%86%99%E7%9C%9F%E7%B7%A8%E9%9B%86%E3%82%A2%E3%83%97%E3%83%AA\u002Fid561350520",[203],"公式アプリ（app store）",[199,1434,1437],{"href":1435,"rel":1436},"https:\u002F\u002Fplay.google.com\u002Fstore\u002Fapps\u002Fdetails?id=com.gopro.smarty&hl=ja&gl=US",[203],"公式アプリ（android）",[18,1439,1440,1441],{},"goproとアプリを接続して、「ライブの設定」に移動\n",[85,1442],{":src":1443,":width":1444,":center":1445},"'rtmp-docker-local\u002Fg1.jpg'","'200px'","true",[18,1447,1448,1449],{},"その他、RTMPを選択\n",[85,1450],{":src":1451,":width":1444,":center":1445},"'rtmp-docker-local\u002Fg2.jpg'",[18,1453,1454,1455,1457,1458],{},"Macと同じwi-fiに接続して",[136,1456,1411],{},"をURLに入力\n",[85,1459],{":src":1460,":width":1444,":center":1445},"'rtmp-docker-local\u002Fg3.jpg'",[18,1462,1463],{},"設定が完了したらライブ配信を開始します。",[45,1465,1466],{"id":1466},"サーバーでは",[18,1468,1469],{},"ライブ配信を開始するとRTMPのURLへ動画がアップされていきます。サーバーの方ではドキュメントルートにこの映像のHLSが配置されるようにしたので、マウントの関係上以下のようなディレクトリが発生していると思います。",[85,1471],{":src":1472,":width":1473,":center":1445},"'rtmp-docker-local\u002Fserver.png'","'100px'",[18,1475,1476,1477,1479],{},"先程のHTMLにあった「",[136,1478,1350],{},"」はサーバーにアップロードされた映像の元でもあるファイルを示しています。",[45,1481,1483],{"id":1482},"ブラウザを見てみると","ブラウザを見てみると..",[18,1485,1486,1487,1490],{},"では",[136,1488,1489],{},"http:\u002F\u002Flocalhost:8085\u002Findex.html","でPCから映像を見てみましょう。再生ボタンを押すと以下のようになりました。",[85,1492],{":src":1493,":width":88},"'rtmp-docker-local\u002Fweb.png'",[18,1495,1496],{},"しっかりとGoproが写している映像が表示され、音声も流れました。画面を写していましたが、いろいろカメラを回すと確かに映像・音声も切り替わります。",[18,1498,1499],{},"とりあえずこれでライブ配信映像を自前で実装したサーバに送ることが確認できました。今度はサーバー内での映像をyoutubeのlivestreaming apiなどを通じて実際にライブ映像に流せるようにしてみます。",[25,1501,1502],{"id":1502},"参考",[18,1504,1505],{},"今回参考にした資料です。非常に助かりました。",[32,1507,1508,1515,1522],{},[35,1509,1510],{},[199,1511,1514],{"href":1512,"rel":1513},"https:\u002F\u002Fqiita.com\u002Fkobakazu0429\u002Fitems\u002F33739b83000583c5448e",[203],"docker-nginx-rtmpとiPhoneでお手軽マルチカメラライブストリーミング配信環境の構築",[35,1516,1517],{},[199,1518,1521],{"href":1519,"rel":1520},"https:\u002F\u002Fblog.litus.co.jp\u002F2020\u002F11\u002Fdockerhlswebcentos-nginxffmpegdocker.html",[203],"Dockerコンテナ化させたHLSのwebストリーミング配信環境構築",[35,1523,1524],{},[199,1525,1527],{"href":201,"rel":1526},[203],"Docker image",[1529,1530,1531],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .s-wAU, html code.shiki .s-wAU{--shiki-default:#F07178}html pre.shiki code .sAklC, html code.shiki .sAklC{--shiki-default:#89DDFF}html pre.shiki code .sfyAc, html code.shiki .sfyAc{--shiki-default:#C3E88D}html pre.shiki code .sx098, html code.shiki .sx098{--shiki-default:#F78C6C}html pre.shiki code .sJ14y, html code.shiki .sJ14y{--shiki-default:#C792EA}html pre.shiki code .s0W1g, html code.shiki .s0W1g{--shiki-default:#BABED8}html pre.shiki code .sC9rS, html code.shiki .sC9rS{--shiki-default:#464B5D;--shiki-default-font-style:italic}html pre.shiki code .sdLwU, html code.shiki .sdLwU{--shiki-default:#82AAFF}html pre.shiki code .s7ZW3, html code.shiki .s7ZW3{--shiki-default:#BABED8;--shiki-default-font-style:italic}",{"title":171,"searchDepth":249,"depth":249,"links":1533},[1534,1539,1543,1552,1558],{"id":27,"depth":191,"text":27,"children":1535},[1536,1537,1538],{"id":47,"depth":249,"text":47},{"id":40,"depth":249,"text":40},{"id":77,"depth":249,"text":77},{"id":91,"depth":191,"text":91,"children":1540},[1541,1542],{"id":123,"depth":249,"text":124},{"id":130,"depth":249,"text":131},{"id":142,"depth":191,"text":143,"children":1544},[1545,1546,1550,1551],{"id":146,"depth":249,"text":146},{"id":336,"depth":249,"text":336,"children":1547},[1548,1549],{"id":343,"depth":257,"text":344},{"id":746,"depth":257,"text":746},{"id":967,"depth":249,"text":967},{"id":1385,"depth":249,"text":1385},{"id":1397,"depth":191,"text":1398,"children":1553},[1554,1555,1556,1557],{"id":1401,"depth":249,"text":1402},{"id":1422,"depth":249,"text":1422},{"id":1466,"depth":249,"text":1466},{"id":1482,"depth":249,"text":1483},{"id":1502,"depth":191,"text":1502},[1560],"devstack","2022-05-20","ローカルのDockerとNginxを用いてGoproからライブ配信映像をRTMPを再生する","md",{},"\u002Fseries\u002Frtmp-manager-server-1",{"title":6,"description":1562},"rtmp-manager-server","Goproの配信映像を中継してyoutubeへの配信を管理するwebシステムを開発する","series\u002Frtmp-manager-server-1",[1571,1572],"docker","nginx","rtmp-docker-local\u002Fthumbnail.png",null,"_e2Aj1JaZSf-KJSGe6AqjuPlf5YdLmKDjuCXzhFw0dg",{"id":1577,"title":1578,"body":1579,"category":3434,"createdAt":3435,"description":3436,"extension":1563,"index":191,"meta":3437,"navigation":365,"path":3438,"publish":365,"seo":3439,"series":1567,"seriesTitle":1568,"stem":3440,"tag":3441,"thumbnail":3442,"updatedAt":1574,"__hash__":3443},"series\u002Fseries\u002Frtmp-manager-server-2.md","配信中継サーバ開発2：ffmpegを用いたyoutubeへのテスト配信とローカルデバッグ環境の構築",{"type":8,"value":1580,"toc":3413},[1581,1584,1587,1590,1601,1604,1607,1610,1627,1630,1633,1639,1642,1682,1701,1960,1963,2187,2189,2372,2379,2526,2529,2866,2869,2872,2875,2878,2971,2998,3001,3004,3011,3017,3023,3034,3038,3041,3045,3048,3059,3062,3065,3068,3074,3080,3084,3091,3097,3110,3113,3116,3119,3123,3126,3134,3138,3141,3144,3147,3150,3154,3157,3160,3163,3166,3169,3172,3175,3178,3189,3192,3195,3198,3201,3205,3208,3342,3352,3358,3364,3367,3370,3376,3383,3386,3389,3392,3395,3411],[18,1582,1583],{},"こんにちはjunです。前回の記事から2年も経ってしまいましたが、サイクリング欲が再度湧いてきたのでこの内容を完成させるように頑張ります。ではとりあえず前回はgoproからdockerのRTMPサーバに送信してローカルマシン上で見れるかを確かめました。最終的には以下のような構成を開発します。（その１のものと変わっています）",[85,1585],{":src":1586,":width":88},"'rtmp-docker-local\u002Ffig2.jpg'",[18,1588,1589],{},"今回の記事は",[32,1591,1592,1595,1598],{},[35,1593,1594],{},"上記図の環境をローカルでdockerで構築する",[35,1596,1597],{},"管理用サーバ上のffmpegを用いてyoutubeにgoproの映像を中継してライブする",[35,1599,1600],{},"ローカルで簡単に配信をデバッグできる環境を構築する\nの3点を解説したいと思います。",[18,1602,1603],{},"これらの内容をまずローカルで実装するためにDockerを用いて構築します。使用しているOSはmacOs Ventura 13.4.1でdockerは4.17.0です。また前回から2年たっているのでdockerfileなどを修正します。",[25,1605,1606],{"id":1606},"完成品",[18,1608,1609],{},"この記事でのゴールはこんな感じです。（字幕ON推奨）",[11,1611,1612],{},[1613,1614,1615,1625],"no-ssr",{},[1616,1617],"iframe",{"width":1618,"height":1619,"src":1620,"title":1621,"frameBorder":1622,"allow":1623,"referrerPolicy":1624,"allowFullScreen":365},"100%","400px","https:\u002F\u002Fwww.youtube.com\u002Fembed\u002FTY61fzShv30?si=a72lxLH1mvLsYTma","YouTube video player","0","accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share","strict-origin-when-cross-origin",[1613,1626],{},[25,1628,1629],{"id":1629},"全ファイル構成",[18,1631,1632],{},"とりあえず完成した必要なファイルと構成を載せます。",[163,1634,1637],{"className":1635,"code":1636,"language":168},[166],".\n├── Dockerfile\n├── conf\n│   ├── 000-default.conf\n│   ├── default.conf\n│   └── nginx.conf\n├── debug\n│   └── index.html\n├── docker-compose.yml\n└── src\n    └── public\n        └── test.html\n",[136,1638,1636],{"__ignoreMap":171},[18,1640,1641],{},"Dockerfileは管理用コンテナの構成に変更。あとでwebフレームワークのlumenを入れるためにcomposerを入れています。（この回では入れません）。そしてffmpegを入れておきます。",[163,1643,1645],{"className":177,"code":1644,"language":156,"meta":171,"style":171},"FROM php:apache\nCOPY --from=composer \u002Fusr\u002Fbin\u002Fcomposer \u002Fusr\u002Fbin\u002Fcomposer\nRUN curl -O https:\u002F\u002Fjohnvansickle.com\u002Fffmpeg\u002Freleases\u002Fffmpeg-release-amd64-static.tar.xz && \\\n    tar -xJf ffmpeg-release-amd64-static.tar.xz && \\\n    mv ffmpeg-*\u002Fffmpeg \u002Fusr\u002Flocal\u002Fbin\u002F && \\\n    mv ffmpeg-*\u002Fffprobe \u002Fusr\u002Flocal\u002Fbin\u002F && \\\n    rm -rf ffmpeg-*\n",[136,1646,1647,1652,1657,1662,1667,1672,1677],{"__ignoreMap":171},[182,1648,1649],{"class":184,"line":185},[182,1650,1651],{},"FROM php:apache\n",[182,1653,1654],{"class":184,"line":191},[182,1655,1656],{},"COPY --from=composer \u002Fusr\u002Fbin\u002Fcomposer \u002Fusr\u002Fbin\u002Fcomposer\n",[182,1658,1659],{"class":184,"line":249},[182,1660,1661],{},"RUN curl -O https:\u002F\u002Fjohnvansickle.com\u002Fffmpeg\u002Freleases\u002Fffmpeg-release-amd64-static.tar.xz && \\\n",[182,1663,1664],{"class":184,"line":257},[182,1665,1666],{},"    tar -xJf ffmpeg-release-amd64-static.tar.xz && \\\n",[182,1668,1669],{"class":184,"line":269},[182,1670,1671],{},"    mv ffmpeg-*\u002Fffmpeg \u002Fusr\u002Flocal\u002Fbin\u002F && \\\n",[182,1673,1674],{"class":184,"line":277},[182,1675,1676],{},"    mv ffmpeg-*\u002Fffprobe \u002Fusr\u002Flocal\u002Fbin\u002F && \\\n",[182,1678,1679],{"class":184,"line":292},[182,1680,1681],{},"    rm -rf ffmpeg-*\n",[32,1683,1684,1690,1695],{},[35,1685,1686,1689],{},[136,1687,1688],{},"rtmptarget","がテスト配信先コンテナ",[35,1691,1692,1694],{},[136,1693,343],{},"がGorpoなどからの受信用コンテナ",[35,1696,1697,1700],{},[136,1698,1699],{},"php","が管理用web uiや配信媒体へ送信する処理を行うコンテナ",[163,1702,1706],{"className":1703,"code":1704,"filename":160,"language":1705,"meta":171,"style":171},"language-yml shiki shiki-themes material-theme-ocean","version: '2'\nservices:\n  rtmptarget:\n    image: tiangolo\u002Fnginx-rtmp\n    ports:\n      - \"1930:1935\"\n      - \"1931:80\"\n    volumes:\n      - .\u002Fconf\u002Fnginx.conf:\u002Fetc\u002Fnginx\u002Fnginx.conf\n      - .\u002Fconf\u002Fdefault.conf:\u002Fetc\u002Fnginx\u002Fconf.d\u002Fdefault.conf\n      - .\u002Fdebug:\u002Fusr\u002Fshare\u002Fnginx\u002F\n  rtmp:\n    image: tiangolo\u002Fnginx-rtmp\n    ports:\n      - \"1935:1935\"\n    volumes:\n      - shared_data:\u002Fusr\u002Fshare\u002Fnginx\u002Fhls\n      - .\u002Fconf\u002Fnginx.conf:\u002Fetc\u002Fnginx\u002Fnginx.conf\n  php:\n    build: .\n    expose:\n      - 1935\n    ports:\n      - \"8080:80\"\n    volumes:\n      - shared_data:\u002Fvar\u002Fwww\u002Fhtml\u002Fhls\n      - .\u002Fsrc:\u002Fvar\u002Fwww\u002Fhtml\n      - .\u002Fconf\u002F.htpasswd:\u002Fvar\u002Fwww\u002F.htpasswd\n      - .\u002Fconf\u002F000-default.conf:\u002Fetc\u002Fapache2\u002Fsites-enabled\u002F000-default.conf\n    extra_hosts:\n      - \"host.docker.internal:host-gateway\"\nvolumes:\n  shared_data:\n","yml",[136,1707,1708,1720,1726,1733,1743,1749,1760,1771,1777,1784,1791,1798,1805,1813,1819,1829,1835,1842,1848,1855,1863,1870,1877,1883,1894,1900,1907,1914,1921,1928,1935,1946,1953],{"__ignoreMap":171},[182,1709,1710,1712,1714,1716,1718],{"class":184,"line":185},[182,1711,224],{"class":223},[182,1713,228],{"class":227},[182,1715,231],{"class":227},[182,1717,235],{"class":234},[182,1719,238],{"class":227},[182,1721,1722,1724],{"class":184,"line":191},[182,1723,243],{"class":223},[182,1725,246],{"class":227},[182,1727,1728,1731],{"class":184,"line":249},[182,1729,1730],{"class":223},"  rtmptarget",[182,1732,246],{"class":227},[182,1734,1735,1738,1740],{"class":184,"line":257},[182,1736,1737],{"class":223},"    image",[182,1739,228],{"class":227},[182,1741,1742],{"class":234}," tiangolo\u002Fnginx-rtmp\n",[182,1744,1745,1747],{"class":184,"line":269},[182,1746,272],{"class":223},[182,1748,246],{"class":227},[182,1750,1751,1753,1755,1758],{"class":184,"line":277},[182,1752,280],{"class":227},[182,1754,283],{"class":227},[182,1756,1757],{"class":234},"1930:1935",[182,1759,289],{"class":227},[182,1761,1762,1764,1766,1769],{"class":184,"line":292},[182,1763,280],{"class":227},[182,1765,283],{"class":227},[182,1767,1768],{"class":234},"1931:80",[182,1770,289],{"class":227},[182,1772,1773,1775],{"class":184,"line":304},[182,1774,307],{"class":223},[182,1776,246],{"class":227},[182,1778,1779,1781],{"class":184,"line":312},[182,1780,280],{"class":227},[182,1782,1783],{"class":234}," .\u002Fconf\u002Fnginx.conf:\u002Fetc\u002Fnginx\u002Fnginx.conf\n",[182,1785,1786,1788],{"class":184,"line":320},[182,1787,280],{"class":227},[182,1789,1790],{"class":234}," .\u002Fconf\u002Fdefault.conf:\u002Fetc\u002Fnginx\u002Fconf.d\u002Fdefault.conf\n",[182,1792,1793,1795],{"class":184,"line":328},[182,1794,280],{"class":227},[182,1796,1797],{"class":234}," .\u002Fdebug:\u002Fusr\u002Fshare\u002Fnginx\u002F\n",[182,1799,1800,1803],{"class":184,"line":411},[182,1801,1802],{"class":223},"  rtmp",[182,1804,246],{"class":227},[182,1806,1807,1809,1811],{"class":184,"line":416},[182,1808,1737],{"class":223},[182,1810,228],{"class":227},[182,1812,1742],{"class":234},[182,1814,1815,1817],{"class":184,"line":422},[182,1816,272],{"class":223},[182,1818,246],{"class":227},[182,1820,1821,1823,1825,1827],{"class":184,"line":428},[182,1822,280],{"class":227},[182,1824,283],{"class":227},[182,1826,299],{"class":234},[182,1828,289],{"class":227},[182,1830,1831,1833],{"class":184,"line":434},[182,1832,307],{"class":223},[182,1834,246],{"class":227},[182,1836,1837,1839],{"class":184,"line":440},[182,1838,280],{"class":227},[182,1840,1841],{"class":234}," shared_data:\u002Fusr\u002Fshare\u002Fnginx\u002Fhls\n",[182,1843,1844,1846],{"class":184,"line":446},[182,1845,280],{"class":227},[182,1847,1783],{"class":234},[182,1849,1850,1853],{"class":184,"line":452},[182,1851,1852],{"class":223},"  php",[182,1854,246],{"class":227},[182,1856,1857,1859,1861],{"class":184,"line":458},[182,1858,260],{"class":223},[182,1860,228],{"class":227},[182,1862,266],{"class":265},[182,1864,1865,1868],{"class":184,"line":463},[182,1866,1867],{"class":223},"    expose",[182,1869,246],{"class":227},[182,1871,1872,1874],{"class":184,"line":469},[182,1873,280],{"class":227},[182,1875,1876],{"class":265}," 1935\n",[182,1878,1879,1881],{"class":184,"line":475},[182,1880,272],{"class":223},[182,1882,246],{"class":227},[182,1884,1885,1887,1889,1892],{"class":184,"line":480},[182,1886,280],{"class":227},[182,1888,283],{"class":227},[182,1890,1891],{"class":234},"8080:80",[182,1893,289],{"class":227},[182,1895,1896,1898],{"class":184,"line":486},[182,1897,307],{"class":223},[182,1899,246],{"class":227},[182,1901,1902,1904],{"class":184,"line":492},[182,1903,280],{"class":227},[182,1905,1906],{"class":234}," shared_data:\u002Fvar\u002Fwww\u002Fhtml\u002Fhls\n",[182,1908,1909,1911],{"class":184,"line":498},[182,1910,280],{"class":227},[182,1912,1913],{"class":234}," .\u002Fsrc:\u002Fvar\u002Fwww\u002Fhtml\n",[182,1915,1916,1918],{"class":184,"line":504},[182,1917,280],{"class":227},[182,1919,1920],{"class":234}," .\u002Fconf\u002F.htpasswd:\u002Fvar\u002Fwww\u002F.htpasswd\n",[182,1922,1923,1925],{"class":184,"line":510},[182,1924,280],{"class":227},[182,1926,1927],{"class":234}," .\u002Fconf\u002F000-default.conf:\u002Fetc\u002Fapache2\u002Fsites-enabled\u002F000-default.conf\n",[182,1929,1930,1933],{"class":184,"line":516},[182,1931,1932],{"class":223},"    extra_hosts",[182,1934,246],{"class":227},[182,1936,1937,1939,1941,1944],{"class":184,"line":522},[182,1938,280],{"class":227},[182,1940,283],{"class":227},[182,1942,1943],{"class":234},"host.docker.internal:host-gateway",[182,1945,289],{"class":227},[182,1947,1948,1951],{"class":184,"line":527},[182,1949,1950],{"class":223},"volumes",[182,1952,246],{"class":227},[182,1954,1955,1958],{"class":184,"line":532},[182,1956,1957],{"class":223},"  shared_data",[182,1959,246],{"class":227},[18,1961,1962],{},"主にRTMPサーバ用の内容です。",[163,1964,1966],{"className":350,"code":1965,"filename":352,"language":353,"meta":171,"style":171},"worker_processes  auto;\n\nerror_log  \u002Fvar\u002Flog\u002Fnginx\u002Ferror.log notice;\npid        \u002Fvar\u002Frun\u002Fnginx.pid;\n\n\nevents {\n    worker_connections  1024;\n}\n\nrtmp_auto_push on;\n\nrtmp {\n    server {\n        listen 1935;\n        listen [::]:1935 ipv6only=on;\n        access_log \u002Fvar\u002Flog\u002Frtmp_access.log;\n        chunk_size 4096;\n        timeout 10s;\n\n        application live {\n            live on;\n\n            # HLSの記述欄\n            hls on;\n            # ここに映像ファイルが配置される\n            hls_path \u002Fusr\u002Fshare\u002Fnginx\u002Fhls;\n            hls_fragment 10s;\n            hls_playlist_length 30s;\n        }\n    }\n}\n\n\n\nhttp {\n    include       \u002Fetc\u002Fnginx\u002Fmime.types;\n    default_type  application\u002Foctet-stream;\n\n    log_format  main  '$remote_addr - $remote_user [$time_local] \"$request\" '\n                      '$status $body_bytes_sent \"$http_referer\" '\n                      '\"$http_user_agent\" \"$http_x_forwarded_for\"';\n\n    access_log  \u002Fvar\u002Flog\u002Fnginx\u002Faccess.log  main;\n\n    sendfile        on;\n    #tcp_nopush     on;\n\n    keepalive_timeout  65;\n\n    #gzip  on;\n\n    include \u002Fetc\u002Fnginx\u002Fconf.d\u002F*.conf;\n}\n",[136,1967,1968,1972,1976,1980,1984,1988,1992,1996,2000,2004,2008,2012,2016,2020,2024,2028,2032,2036,2040,2044,2048,2052,2056,2060,2064,2068,2072,2077,2081,2086,2090,2094,2098,2102,2106,2110,2114,2118,2122,2126,2130,2134,2138,2142,2146,2150,2154,2158,2162,2166,2170,2174,2178,2182],{"__ignoreMap":171},[182,1969,1970],{"class":184,"line":185},[182,1971,360],{},[182,1973,1974],{"class":184,"line":191},[182,1975,366],{"emptyLinePlaceholder":365},[182,1977,1978],{"class":184,"line":249},[182,1979,371],{},[182,1981,1982],{"class":184,"line":257},[182,1983,376],{},[182,1985,1986],{"class":184,"line":269},[182,1987,366],{"emptyLinePlaceholder":365},[182,1989,1990],{"class":184,"line":277},[182,1991,366],{"emptyLinePlaceholder":365},[182,1993,1994],{"class":184,"line":292},[182,1995,389],{},[182,1997,1998],{"class":184,"line":304},[182,1999,394],{},[182,2001,2002],{"class":184,"line":312},[182,2003,399],{},[182,2005,2006],{"class":184,"line":320},[182,2007,366],{"emptyLinePlaceholder":365},[182,2009,2010],{"class":184,"line":328},[182,2011,408],{},[182,2013,2014],{"class":184,"line":411},[182,2015,366],{"emptyLinePlaceholder":365},[182,2017,2018],{"class":184,"line":416},[182,2019,419],{},[182,2021,2022],{"class":184,"line":422},[182,2023,425],{},[182,2025,2026],{"class":184,"line":428},[182,2027,431],{},[182,2029,2030],{"class":184,"line":434},[182,2031,437],{},[182,2033,2034],{"class":184,"line":440},[182,2035,443],{},[182,2037,2038],{"class":184,"line":446},[182,2039,449],{},[182,2041,2042],{"class":184,"line":452},[182,2043,455],{},[182,2045,2046],{"class":184,"line":458},[182,2047,366],{"emptyLinePlaceholder":365},[182,2049,2050],{"class":184,"line":463},[182,2051,466],{},[182,2053,2054],{"class":184,"line":469},[182,2055,472],{},[182,2057,2058],{"class":184,"line":475},[182,2059,366],{"emptyLinePlaceholder":365},[182,2061,2062],{"class":184,"line":480},[182,2063,483],{},[182,2065,2066],{"class":184,"line":486},[182,2067,489],{},[182,2069,2070],{"class":184,"line":492},[182,2071,495],{},[182,2073,2074],{"class":184,"line":498},[182,2075,2076],{},"            hls_path \u002Fusr\u002Fshare\u002Fnginx\u002Fhls;\n",[182,2078,2079],{"class":184,"line":504},[182,2080,507],{},[182,2082,2083],{"class":184,"line":510},[182,2084,2085],{},"            hls_playlist_length 30s;\n",[182,2087,2088],{"class":184,"line":516},[182,2089,513],{},[182,2091,2092],{"class":184,"line":522},[182,2093,519],{},[182,2095,2096],{"class":184,"line":527},[182,2097,399],{},[182,2099,2100],{"class":184,"line":532},[182,2101,366],{"emptyLinePlaceholder":365},[182,2103,2104],{"class":184,"line":537},[182,2105,366],{"emptyLinePlaceholder":365},[182,2107,2108],{"class":184,"line":542},[182,2109,366],{"emptyLinePlaceholder":365},[182,2111,2112],{"class":184,"line":548},[182,2113,545],{},[182,2115,2116],{"class":184,"line":554},[182,2117,551],{},[182,2119,2120],{"class":184,"line":560},[182,2121,557],{},[182,2123,2124],{"class":184,"line":565},[182,2125,366],{"emptyLinePlaceholder":365},[182,2127,2128],{"class":184,"line":571},[182,2129,568],{},[182,2131,2132],{"class":184,"line":577},[182,2133,574],{},[182,2135,2136],{"class":184,"line":583},[182,2137,580],{},[182,2139,2140],{"class":184,"line":588},[182,2141,366],{"emptyLinePlaceholder":365},[182,2143,2144],{"class":184,"line":594},[182,2145,591],{},[182,2147,2148],{"class":184,"line":599},[182,2149,366],{"emptyLinePlaceholder":365},[182,2151,2152],{"class":184,"line":605},[182,2153,602],{},[182,2155,2156],{"class":184,"line":611},[182,2157,608],{},[182,2159,2160],{"class":184,"line":616},[182,2161,366],{"emptyLinePlaceholder":365},[182,2163,2164],{"class":184,"line":622},[182,2165,619],{},[182,2167,2168],{"class":184,"line":627},[182,2169,366],{"emptyLinePlaceholder":365},[182,2171,2172],{"class":184,"line":633},[182,2173,630],{},[182,2175,2176],{"class":184,"line":638},[182,2177,366],{"emptyLinePlaceholder":365},[182,2179,2180],{"class":184,"line":644},[182,2181,641],{},[182,2183,2185],{"class":184,"line":2184},54,[182,2186,399],{},[18,2188,1962],{},[163,2190,2193],{"className":350,"code":2191,"filename":2192,"language":353,"meta":171,"style":171},"server {\n    listen       80;\n    listen  [::]:80;\n    server_name  localhost;\n\n    #access_log  \u002Fvar\u002Flog\u002Fnginx\u002Fhost.access.log  main;\n\n    location \u002F {\n        root   \u002Fusr\u002Fshare\u002Fnginx;\n        index  index.html index.htm;\n    }\n\n    #error_page  404              \u002F404.html;\n\n    # redirect server error pages to the static page \u002F50x.html\n    #\n    error_page   500 502 503 504  \u002F50x.html;\n    location = \u002F50x.html {\n        root   \u002Fusr\u002Fshare\u002Fnginx;\n    }\n\n    # proxy the PHP scripts to Apache listening on 127.0.0.1:80\n    #\n    #location ~ \\.php$ {\n    #    proxy_pass   http:\u002F\u002F127.0.0.1;\n    #}\n\n    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000\n    #\n    #location ~ \\.php$ {\n    #    root           html;\n    #    fastcgi_pass   127.0.0.1:9000;\n    #    fastcgi_index  index.php;\n    #    fastcgi_param  SCRIPT_FILENAME  \u002Fscripts$fastcgi_script_name;\n    #    include        fastcgi_params;\n    #}\n\n    # deny access to .htaccess files, if Apache's document root\n    # concurs with nginx's one\n    #\n    #location ~ \u002F\\.ht {\n    #    deny  all;\n    #}\n}\n","conf\u002Fdefault.conf",[136,2194,2195,2199,2203,2207,2211,2215,2219,2223,2227,2232,2236,2240,2244,2248,2252,2256,2260,2264,2268,2272,2276,2280,2284,2288,2292,2296,2300,2304,2308,2312,2316,2320,2324,2328,2332,2336,2340,2344,2348,2352,2356,2360,2364,2368],{"__ignoreMap":171},[182,2196,2197],{"class":184,"line":185},[182,2198,763],{},[182,2200,2201],{"class":184,"line":191},[182,2202,768],{},[182,2204,2205],{"class":184,"line":249},[182,2206,773],{},[182,2208,2209],{"class":184,"line":257},[182,2210,778],{},[182,2212,2213],{"class":184,"line":269},[182,2214,366],{"emptyLinePlaceholder":365},[182,2216,2217],{"class":184,"line":277},[182,2218,787],{},[182,2220,2221],{"class":184,"line":292},[182,2222,366],{"emptyLinePlaceholder":365},[182,2224,2225],{"class":184,"line":304},[182,2226,796],{},[182,2228,2229],{"class":184,"line":312},[182,2230,2231],{},"        root   \u002Fusr\u002Fshare\u002Fnginx;\n",[182,2233,2234],{"class":184,"line":320},[182,2235,806],{},[182,2237,2238],{"class":184,"line":328},[182,2239,519],{},[182,2241,2242],{"class":184,"line":411},[182,2243,366],{"emptyLinePlaceholder":365},[182,2245,2246],{"class":184,"line":416},[182,2247,819],{},[182,2249,2250],{"class":184,"line":422},[182,2251,366],{"emptyLinePlaceholder":365},[182,2253,2254],{"class":184,"line":428},[182,2255,828],{},[182,2257,2258],{"class":184,"line":434},[182,2259,833],{},[182,2261,2262],{"class":184,"line":440},[182,2263,838],{},[182,2265,2266],{"class":184,"line":446},[182,2267,843],{},[182,2269,2270],{"class":184,"line":452},[182,2271,2231],{},[182,2273,2274],{"class":184,"line":458},[182,2275,519],{},[182,2277,2278],{"class":184,"line":463},[182,2279,366],{"emptyLinePlaceholder":365},[182,2281,2282],{"class":184,"line":469},[182,2283,860],{},[182,2285,2286],{"class":184,"line":475},[182,2287,833],{},[182,2289,2290],{"class":184,"line":480},[182,2291,869],{},[182,2293,2294],{"class":184,"line":486},[182,2295,874],{},[182,2297,2298],{"class":184,"line":492},[182,2299,879],{},[182,2301,2302],{"class":184,"line":498},[182,2303,366],{"emptyLinePlaceholder":365},[182,2305,2306],{"class":184,"line":504},[182,2307,888],{},[182,2309,2310],{"class":184,"line":510},[182,2311,833],{},[182,2313,2314],{"class":184,"line":516},[182,2315,869],{},[182,2317,2318],{"class":184,"line":522},[182,2319,901],{},[182,2321,2322],{"class":184,"line":527},[182,2323,906],{},[182,2325,2326],{"class":184,"line":532},[182,2327,911],{},[182,2329,2330],{"class":184,"line":537},[182,2331,916],{},[182,2333,2334],{"class":184,"line":542},[182,2335,921],{},[182,2337,2338],{"class":184,"line":548},[182,2339,879],{},[182,2341,2342],{"class":184,"line":554},[182,2343,366],{"emptyLinePlaceholder":365},[182,2345,2346],{"class":184,"line":560},[182,2347,934],{},[182,2349,2350],{"class":184,"line":565},[182,2351,939],{},[182,2353,2354],{"class":184,"line":571},[182,2355,833],{},[182,2357,2358],{"class":184,"line":577},[182,2359,948],{},[182,2361,2362],{"class":184,"line":583},[182,2363,953],{},[182,2365,2366],{"class":184,"line":588},[182,2367,879],{},[182,2369,2370],{"class":184,"line":594},[182,2371,399],{},[18,2373,2374,2375,2378],{},"管理用コンテナに使用します。ドキュメントルートが",[136,2376,2377],{},"\u002Fvar\u002Fwww\u002Fhtml\u002Fpublic","なのは後でlumenを入れるためです。",[163,2380,2383],{"className":350,"code":2381,"filename":2382,"language":353,"meta":171,"style":171},"\u003CVirtualHost *:80>\n    # The ServerName directive sets the request scheme, hostname and port that\n    # the server uses to identify itself. This is used when creating\n    # redirection URLs. In the context of virtual hosts, the ServerName\n    # specifies what hostname must appear in the request's Host: header to\n    # match this virtual host. For the default virtual host (this file) this\n    # value is not decisive as it is used as a last resort host regardless.\n    # However, you must set it for any further virtual host explicitly.\n    #ServerName www.example.com\n\n    ServerAdmin webmaster@localhost\n    DocumentRoot \u002Fvar\u002Fwww\u002Fhtml\u002Fpublic\n\n    # Available loglevels: trace8, ..., trace1, debug, info, notice, warn,\n    # error, crit, alert, emerg.\n    # It is also possible to configure the loglevel for particular\n    # modules, e.g.\n    #LogLevel info ssl:warn\n\n    ErrorLog ${APACHE_LOG_DIR}\u002Ferror.log\n    CustomLog ${APACHE_LOG_DIR}\u002Faccess.log combined\n\n    # For most configuration files from conf-available\u002F, which are\n    # enabled or disabled at a global level, it is possible to\n    # include a line for only one particular virtual host. For example the\n    # following line enables the CGI configuration for this host only\n    # after it has been globally disabled with \"a2disconf\".\n    #Include conf-available\u002Fserve-cgi-bin.conf\n\u003C\u002FVirtualHost>\n","conf\u002F000-default.conf",[136,2384,2385,2390,2395,2400,2405,2410,2415,2420,2425,2430,2434,2439,2444,2448,2453,2458,2463,2468,2473,2477,2482,2487,2491,2496,2501,2506,2511,2516,2521],{"__ignoreMap":171},[182,2386,2387],{"class":184,"line":185},[182,2388,2389],{},"\u003CVirtualHost *:80>\n",[182,2391,2392],{"class":184,"line":191},[182,2393,2394],{},"    # The ServerName directive sets the request scheme, hostname and port that\n",[182,2396,2397],{"class":184,"line":249},[182,2398,2399],{},"    # the server uses to identify itself. This is used when creating\n",[182,2401,2402],{"class":184,"line":257},[182,2403,2404],{},"    # redirection URLs. In the context of virtual hosts, the ServerName\n",[182,2406,2407],{"class":184,"line":269},[182,2408,2409],{},"    # specifies what hostname must appear in the request's Host: header to\n",[182,2411,2412],{"class":184,"line":277},[182,2413,2414],{},"    # match this virtual host. For the default virtual host (this file) this\n",[182,2416,2417],{"class":184,"line":292},[182,2418,2419],{},"    # value is not decisive as it is used as a last resort host regardless.\n",[182,2421,2422],{"class":184,"line":304},[182,2423,2424],{},"    # However, you must set it for any further virtual host explicitly.\n",[182,2426,2427],{"class":184,"line":312},[182,2428,2429],{},"    #ServerName www.example.com\n",[182,2431,2432],{"class":184,"line":320},[182,2433,366],{"emptyLinePlaceholder":365},[182,2435,2436],{"class":184,"line":328},[182,2437,2438],{},"    ServerAdmin webmaster@localhost\n",[182,2440,2441],{"class":184,"line":411},[182,2442,2443],{},"    DocumentRoot \u002Fvar\u002Fwww\u002Fhtml\u002Fpublic\n",[182,2445,2446],{"class":184,"line":416},[182,2447,366],{"emptyLinePlaceholder":365},[182,2449,2450],{"class":184,"line":422},[182,2451,2452],{},"    # Available loglevels: trace8, ..., trace1, debug, info, notice, warn,\n",[182,2454,2455],{"class":184,"line":428},[182,2456,2457],{},"    # error, crit, alert, emerg.\n",[182,2459,2460],{"class":184,"line":434},[182,2461,2462],{},"    # It is also possible to configure the loglevel for particular\n",[182,2464,2465],{"class":184,"line":440},[182,2466,2467],{},"    # modules, e.g.\n",[182,2469,2470],{"class":184,"line":446},[182,2471,2472],{},"    #LogLevel info ssl:warn\n",[182,2474,2475],{"class":184,"line":452},[182,2476,366],{"emptyLinePlaceholder":365},[182,2478,2479],{"class":184,"line":458},[182,2480,2481],{},"    ErrorLog ${APACHE_LOG_DIR}\u002Ferror.log\n",[182,2483,2484],{"class":184,"line":463},[182,2485,2486],{},"    CustomLog ${APACHE_LOG_DIR}\u002Faccess.log combined\n",[182,2488,2489],{"class":184,"line":469},[182,2490,366],{"emptyLinePlaceholder":365},[182,2492,2493],{"class":184,"line":475},[182,2494,2495],{},"    # For most configuration files from conf-available\u002F, which are\n",[182,2497,2498],{"class":184,"line":480},[182,2499,2500],{},"    # enabled or disabled at a global level, it is possible to\n",[182,2502,2503],{"class":184,"line":486},[182,2504,2505],{},"    # include a line for only one particular virtual host. For example the\n",[182,2507,2508],{"class":184,"line":492},[182,2509,2510],{},"    # following line enables the CGI configuration for this host only\n",[182,2512,2513],{"class":184,"line":498},[182,2514,2515],{},"    # after it has been globally disabled with \"a2disconf\".\n",[182,2517,2518],{"class":184,"line":504},[182,2519,2520],{},"    #Include conf-available\u002Fserve-cgi-bin.conf\n",[182,2522,2523],{"class":184,"line":510},[182,2524,2525],{},"\u003C\u002FVirtualHost>\n",[18,2527,2528],{},"とりあえず確認したい時に使用します。",[163,2530,2533],{"className":976,"code":2531,"filename":2532,"language":979,"meta":171,"style":171},"\u003C!DOCTYPE html>\n\u003Chtml>\n\n\u003Chead>\n  \u003Cmeta charset=\"utf-8\">\n  \u003Ctitle>MediaElement\u003C\u002Ftitle>\n  \u003C!-- MediaElement style -->\n  \u003Clink rel=\"stylesheet\" href=\"\u002F\u002Fcdnjs.cloudflare.com\u002Fajax\u002Flibs\u002Fmediaelement\u002F4.2.9\u002Fmediaelementplayer.css\" \u002F>\n\u003C\u002Fhead>\n\n\u003Cbody>\n  \u003C!-- MediaElement -->\n  \u003Cscript src=\"\u002F\u002Fcdnjs.cloudflare.com\u002Fajax\u002Flibs\u002Fmediaelement\u002F4.2.9\u002Fmediaelement-and-player.js\">\u003C\u002Fscript>\n\n  \u003Cvideo id=\"player\" width=\"640\" height=\"360\">\n\u003C\u002Fbody>\n\u003Cscript type=\"text\u002Fjavascript\">\n\n      var player = new MediaElementPlayer('player', {\n        success: function(mediaElement, originalNode) {\n          console.log(\"Player initialised\");\n        }\n      });\n        player.setSrc(\"\u002Fhls\u002F.m3u8\");\n\u003C\u002Fscript>\n\n\u003C\u002Fhtml>\n","src\u002Fpublic\u002Ftest.html,debug\u002Findex.html",[136,2534,2535,2545,2553,2557,2565,2583,2599,2603,2631,2639,2643,2651,2655,2677,2681,2719,2727,2745,2749,2773,2793,2813,2817,2825,2846,2854,2858],{"__ignoreMap":171},[182,2536,2537,2539,2541,2543],{"class":184,"line":185},[182,2538,986],{"class":227},[182,2540,989],{"class":223},[182,2542,993],{"class":992},[182,2544,996],{"class":227},[182,2546,2547,2549,2551],{"class":184,"line":191},[182,2548,1001],{"class":227},[182,2550,979],{"class":223},[182,2552,996],{"class":227},[182,2554,2555],{"class":184,"line":249},[182,2556,366],{"emptyLinePlaceholder":365},[182,2558,2559,2561,2563],{"class":184,"line":257},[182,2560,1001],{"class":227},[182,2562,1016],{"class":223},[182,2564,996],{"class":227},[182,2566,2567,2569,2571,2573,2575,2577,2579,2581],{"class":184,"line":269},[182,2568,1023],{"class":227},[182,2570,1026],{"class":223},[182,2572,1029],{"class":992},[182,2574,1032],{"class":227},[182,2576,1035],{"class":227},[182,2578,1038],{"class":234},[182,2580,1035],{"class":227},[182,2582,996],{"class":227},[182,2584,2585,2587,2589,2591,2593,2595,2597],{"class":184,"line":277},[182,2586,1023],{"class":227},[182,2588,1049],{"class":223},[182,2590,1052],{"class":227},[182,2592,1056],{"class":1055},[182,2594,1059],{"class":227},[182,2596,1049],{"class":223},[182,2598,996],{"class":227},[182,2600,2601],{"class":184,"line":292},[182,2602,1069],{"class":1068},[182,2604,2605,2607,2609,2611,2613,2615,2617,2619,2621,2623,2625,2627,2629],{"class":184,"line":304},[182,2606,1023],{"class":227},[182,2608,1076],{"class":223},[182,2610,1079],{"class":992},[182,2612,1032],{"class":227},[182,2614,1035],{"class":227},[182,2616,1086],{"class":234},[182,2618,1035],{"class":227},[182,2620,1091],{"class":992},[182,2622,1032],{"class":227},[182,2624,1035],{"class":227},[182,2626,1098],{"class":234},[182,2628,1035],{"class":227},[182,2630,1103],{"class":227},[182,2632,2633,2635,2637],{"class":184,"line":312},[182,2634,1059],{"class":227},[182,2636,1016],{"class":223},[182,2638,996],{"class":227},[182,2640,2641],{"class":184,"line":320},[182,2642,366],{"emptyLinePlaceholder":365},[182,2644,2645,2647,2649],{"class":184,"line":328},[182,2646,1001],{"class":227},[182,2648,1122],{"class":223},[182,2650,996],{"class":227},[182,2652,2653],{"class":184,"line":411},[182,2654,1129],{"class":1068},[182,2656,2657,2659,2661,2663,2665,2667,2669,2671,2673,2675],{"class":184,"line":416},[182,2658,1023],{"class":227},[182,2660,1136],{"class":223},[182,2662,1139],{"class":992},[182,2664,1032],{"class":227},[182,2666,1035],{"class":227},[182,2668,1146],{"class":234},[182,2670,1035],{"class":227},[182,2672,1151],{"class":227},[182,2674,1136],{"class":223},[182,2676,996],{"class":227},[182,2678,2679],{"class":184,"line":422},[182,2680,366],{"emptyLinePlaceholder":365},[182,2682,2683,2685,2687,2689,2691,2693,2695,2697,2699,2701,2703,2705,2707,2709,2711,2713,2715,2717],{"class":184,"line":428},[182,2684,1023],{"class":227},[182,2686,1166],{"class":223},[182,2688,1169],{"class":992},[182,2690,1032],{"class":227},[182,2692,1035],{"class":227},[182,2694,1176],{"class":234},[182,2696,1035],{"class":227},[182,2698,1181],{"class":992},[182,2700,1032],{"class":227},[182,2702,1035],{"class":227},[182,2704,1188],{"class":234},[182,2706,1035],{"class":227},[182,2708,1193],{"class":992},[182,2710,1032],{"class":227},[182,2712,1035],{"class":227},[182,2714,1200],{"class":234},[182,2716,1035],{"class":227},[182,2718,996],{"class":227},[182,2720,2721,2723,2725],{"class":184,"line":434},[182,2722,1059],{"class":227},[182,2724,1122],{"class":223},[182,2726,996],{"class":227},[182,2728,2729,2731,2733,2735,2737,2739,2741,2743],{"class":184,"line":440},[182,2730,1001],{"class":227},[182,2732,1136],{"class":223},[182,2734,1221],{"class":992},[182,2736,1032],{"class":227},[182,2738,1035],{"class":227},[182,2740,1228],{"class":234},[182,2742,1035],{"class":227},[182,2744,996],{"class":227},[182,2746,2747],{"class":184,"line":446},[182,2748,366],{"emptyLinePlaceholder":365},[182,2750,2751,2753,2755,2757,2759,2761,2763,2765,2767,2769,2771],{"class":184,"line":452},[182,2752,1241],{"class":992},[182,2754,1244],{"class":1055},[182,2756,1032],{"class":227},[182,2758,1249],{"class":227},[182,2760,1253],{"class":1252},[182,2762,1256],{"class":1055},[182,2764,1259],{"class":227},[182,2766,1176],{"class":234},[182,2768,1259],{"class":227},[182,2770,1266],{"class":227},[182,2772,1269],{"class":227},[182,2774,2775,2777,2779,2781,2783,2785,2787,2789,2791],{"class":184,"line":458},[182,2776,1274],{"class":1252},[182,2778,228],{"class":227},[182,2780,1279],{"class":992},[182,2782,1256],{"class":227},[182,2784,1285],{"class":1284},[182,2786,1266],{"class":227},[182,2788,1290],{"class":1284},[182,2790,1293],{"class":227},[182,2792,1269],{"class":227},[182,2794,2795,2797,2799,2801,2803,2805,2807,2809,2811],{"class":184,"line":463},[182,2796,1300],{"class":1055},[182,2798,1303],{"class":227},[182,2800,1306],{"class":1252},[182,2802,1256],{"class":223},[182,2804,1035],{"class":227},[182,2806,1313],{"class":234},[182,2808,1035],{"class":227},[182,2810,1293],{"class":223},[182,2812,1320],{"class":227},[182,2814,2815],{"class":184,"line":469},[182,2816,513],{"class":227},[182,2818,2819,2821,2823],{"class":184,"line":475},[182,2820,1329],{"class":227},[182,2822,1293],{"class":1055},[182,2824,1320],{"class":227},[182,2826,2827,2829,2831,2833,2835,2837,2840,2842,2844],{"class":184,"line":480},[182,2828,1338],{"class":1055},[182,2830,1303],{"class":227},[182,2832,1343],{"class":1252},[182,2834,1256],{"class":1055},[182,2836,1035],{"class":227},[182,2838,2839],{"class":234},"\u002Fhls\u002F.m3u8",[182,2841,1035],{"class":227},[182,2843,1293],{"class":1055},[182,2845,1320],{"class":227},[182,2847,2848,2850,2852],{"class":184,"line":486},[182,2849,1059],{"class":227},[182,2851,1136],{"class":223},[182,2853,996],{"class":227},[182,2855,2856],{"class":184,"line":492},[182,2857,366],{"emptyLinePlaceholder":365},[182,2859,2860,2862,2864],{"class":184,"line":498},[182,2861,1059],{"class":227},[182,2863,979],{"class":223},[182,2865,996],{"class":227},[18,2867,2868],{},"では上記のファイルを用いて解説します。",[25,2870,2871],{"id":2871},"共有ボリュームを用いて受信した映像を扱う",[18,2873,2874],{},"まずは受信用コンテナから得られたGoproの映像を管理用コンテナで使用できるようにします。受信用コンテナにPHPなどを入れようとしましたが結構面倒だったので、別のコンテナとして独立させました。しかしそのままでは受信した映像データ（HLS）を共有できないため、受信用コンテナで生成されるHLSファイルを共有ボリュームで共有します。",[18,2876,2877],{},"その設定のために",[163,2879,2881],{"className":1703,"code":2880,"filename":160,"language":1705,"meta":171,"style":171},"version: '2'\nservices:\n  #...\n  rtmp:\n    #...\n    volumes:\n      - shared_data:\u002Fusr\u002Fshare\u002Fnginx\u002Fhls\n    #...\n  php:\n    #...\n    volumes:\n      - shared_data:\u002Fvar\u002Fwww\u002Fhtml\u002Fhls\n    #...\nvolumes:\n  shared_data:\n",[136,2882,2883,2895,2901,2906,2912,2917,2923,2929,2933,2939,2943,2949,2955,2959,2965],{"__ignoreMap":171},[182,2884,2885,2887,2889,2891,2893],{"class":184,"line":185},[182,2886,224],{"class":223},[182,2888,228],{"class":227},[182,2890,231],{"class":227},[182,2892,235],{"class":234},[182,2894,238],{"class":227},[182,2896,2897,2899],{"class":184,"line":191},[182,2898,243],{"class":223},[182,2900,246],{"class":227},[182,2902,2903],{"class":184,"line":249},[182,2904,2905],{"class":1068},"  #...\n",[182,2907,2908,2910],{"class":184,"line":257},[182,2909,1802],{"class":223},[182,2911,246],{"class":227},[182,2913,2914],{"class":184,"line":269},[182,2915,2916],{"class":1068},"    #...\n",[182,2918,2919,2921],{"class":184,"line":277},[182,2920,307],{"class":223},[182,2922,246],{"class":227},[182,2924,2925,2927],{"class":184,"line":292},[182,2926,280],{"class":227},[182,2928,1841],{"class":234},[182,2930,2931],{"class":184,"line":304},[182,2932,2916],{"class":1068},[182,2934,2935,2937],{"class":184,"line":312},[182,2936,1852],{"class":223},[182,2938,246],{"class":227},[182,2940,2941],{"class":184,"line":320},[182,2942,2916],{"class":1068},[182,2944,2945,2947],{"class":184,"line":328},[182,2946,307],{"class":223},[182,2948,246],{"class":227},[182,2950,2951,2953],{"class":184,"line":411},[182,2952,280],{"class":227},[182,2954,1906],{"class":234},[182,2956,2957],{"class":184,"line":416},[182,2958,2916],{"class":1068},[182,2960,2961,2963],{"class":184,"line":422},[182,2962,1950],{"class":223},[182,2964,246],{"class":227},[182,2966,2967,2969],{"class":184,"line":428},[182,2968,1957],{"class":223},[182,2970,246],{"class":227},[18,2972,2973,2975,2976,2978,2979,2982,2983,2986,2987,2990,2991,2993,2994,2997],{},[136,2974,1950],{},"でボリュームを定義してそのボリューム名を各コンテナに配置するパスを指定します。すると",[136,2977,343],{},"（受信用コンテナ）では",[136,2980,2981],{},"\u002Fusr\u002Fshare\u002Fnginx\u002Fhls"," に",[136,2984,2985],{},"*.ts","ファイルや",[136,2988,2989],{},".m3u8","ファイルが作成されます。そして",[136,2992,1699],{},"（管理用コンテナ）では",[136,2995,2996],{},"\u002Fvar\u002Fwww\u002Fhtml\u002Fhls","に同じファイル群が作成されます。",[18,2999,3000],{},"管理用コンテナでgoproから受信したデータをffmpegで色々したり、読み取ったり、配信できるようになりました。",[45,3002,3003],{"id":3003},"テストしてみる",[18,3005,3006,3007,3010],{},"その１の記事または後述するOBSの方法を参考にGopro・OBSから",[136,3008,3009],{},"rtmp:\u002F\u002FYOUR_PC_IP\u002Flive"," にRTMP送信をします。",[163,3012,3015],{"className":3013,"code":3014,"language":168},[166],"# \u003CYOUR_PHP_CONTAINER_NAME>はあなたの環境の管理用コンテナ名に書き換えてください。\n\ndocker exec -it \u003CYOUR_PHP_CONTAINER_NAME> \u002Fbin\u002Fbash\n",[136,3016,3014],{"__ignoreMap":171},[18,3018,3019,3020,3022],{},"ボリュームのため、",[136,3021,2996],{},"というディレクトリがあると思います。そしてGoproでの収録を開始するとディレクトリ内にファイルが配置されます。",[18,3024,3025,3026,3029,3030,3033],{},"そして",[136,3027,3028],{},"\u002Fvar\u002Fwww\u002Fhtml\u002Fpublic\u002Ftest.html","があるので",[136,3031,3032],{},"http:\u002F\u002Flocalhost:8080","でweb playerを用いて確認できるはずです。",[25,3035,3037],{"id":3036},"ffmpegを用いてyoutubeに送信してみる","ffmpegを用いてYoutubeに送信してみる",[18,3039,3040],{},"次にffmpegを用いて管理用コンテナからyoutube liveに流してみます。ffmpegはlinux上で動画、音声の変換・記録・再生・配信を行うことができるOSSです。今はコマンド上で配信をするだけですが、最終的にはffmpegを利用して管理画面から配信内容を制御する予定です。現時点で自分もまだ詳細なプロパティーや映像系の知識に乏しいので、調べた際のコマンドのコピペやchat gptで調査した内容になります。",[45,3042,3044],{"id":3043},"youtube-のライブを始めておく","youtube のライブを始めておく",[18,3046,3047],{},"本記事では簡単に述べておきます。すでにGoogle、Youtubeアカウントがありライブストリーミングが有効になっている前提とします。",[96,3049,3050,3053,3056],{},[35,3051,3052],{},"Youtubeに移動し、右上の「＋」ボタンをクリックして「ライブ配信を開始」を選択",[35,3054,3055],{},"ライブ配信の設定からストリームURLとストリームキーを控えます。ライブストリームキーとURLは「rtmp:\u002F\u002Fa.rtmp.youtube.com\u002Flive2\u002FLIVE_KEY」とURLの後につければOKです。ただしドメインのとこはyoutubeのrtmp配信サーバのipにしておいてください（調べ方と理由は後述）",[35,3057,3058],{},"Youtubeのライブ管理画面では「ライブ配信を公開するには、ストリーミング ソフトで動画の送信を開始します」という表示がされています。ffmpegでRTMP送信をすれば表示されます。",[18,3060,3061],{},"プライバシーを非公開にしておくことをおすすめします。",[45,3063,3064],{"id":3064},"コマンドを打つ",[18,3066,3067],{},"Goproからの映像をローカルで確認できたらとりあえず以下のコマンドを管理用コンテナ内から打ってみてください。",[163,3069,3072],{"className":3070,"code":3071,"language":168},[166],"ffmpeg -re -i \"\u002Fvar\u002Fwww\u002Fhtml\u002Fhls\u002F.m3u8\" -c:a aac -ar 44100 -ab 128k -ac 2 -strict -2 -flags +global_header -bsf:a aac_adtstoasc -f flv \"rtmp:\u002F\u002F64.233.189.134\u002Flive2\u002FYOUR_LIVE_KEY\"\n",[136,3073,3071],{"__ignoreMap":171},[18,3075,3076,3079],{},[136,3077,3078],{},"YOUR_LIVE_KEY"," はyoutuebのライブストリームキーを使用してください。ffmpegが起動してyoutubeにデータが送信されます。20秒ぐらいするとGoproからの映像の配信が開始されるはずです。",[45,3081,3083],{"id":3082},"dnsエラー","DNSエラー？",[18,3085,3086,3087,3090],{},"本来ライブストリームURLには",[136,3088,3089],{},"a.rtmp.youtube.com","というドメインを使用すべきですが、私のdockerで管理用コンテナから実行するとffmpegが以下のようなDNSエラーが発生します。",[163,3092,3095],{"className":3093,"code":3094,"language":168},[166],"[tcp @ 0x696d2c0] Failed to resolve hostname a.rtmp.youtube.com: System error\n[rtmp @ 0x696d480] Cannot open connection tcp:\u002F\u002Fa.rtmp.youtube.com:1935?tcp_nodelay=0\n[out#0\u002Fflv @ 0x6890880] Error opening output rtmp:\u002F\u002Fa.rtmp.youtube.com\u002Flive2\u002Fefth-usbu-ye2g-f0sk-a7ta: Input\u002Foutput error\nError opening output file rtmp:\u002F\u002Fa.rtmp.youtube.com\u002Flive2\u002Fefth-usbu-ye2g-f0sk-a7ta.\nError opening output files: Input\u002Foutput error\n",[136,3096,3094],{"__ignoreMap":171},[18,3098,3099,3100,3102,3103,3106,3107,3109],{},"ホストマシン上でffmpegを使用すると問題なく配信できるますが、docker内から行うとなぜか",[136,3101,3089],{},"のホストが見つからないというエラーが発生します。そのためホストマシンで",[136,3104,3105],{},"nslookup","を使用して",[136,3108,3089],{},"のIPを調べて、直接IPを指定したら問題なく動作しました。",[18,3111,3112],{},"ここはdockerの問題かつまだ調査中です。Googleなどはこの辺のIPをころころ変えてきそうなので、最終的にはきちんとドメインを利用できるようにしますがとりあえず今はIPで指定します。",[25,3114,3115],{"id":3115},"ローカルでデバッグしやすい環境にする",[18,3117,3118],{},"ひととおり受信→中継→配信を行えるdockerコンテナが構築できました。しかし毎回goproを起動したり、youtube liveを開始するのは面倒ですし、複数人で開発する時に機材がなかったり送信先が重複してします。そのため開発用にデバッグできるように送信元、配信先をローカルで実現できるようにします。",[45,3120,3122],{"id":3121},"送信元にgoproの代わりにobsを利用する","送信元にgoproの代わりにOBSを利用する",[18,3124,3125],{},"この記事では送信元としてGoproを使用してホストマシンの受信用コンテナにRTMPを送信しています。ですがこれはOBSなどの配信ソフトを利用することで簡単に代替できます。",[18,3127,3128,3129],{},"OBSはフリーのライブ配信や録画が行えます。PC上のウィンドウキャプチャをしたり、機材をそろえればnintedo switchなどのゲーム機の映像を配信することができます。とりあえずOBSをインストールして、webカメラなりウィンドウキャプチャを映像・音声ソースとして利用します。",[199,3130,3133],{"href":3131,"rel":3132},"https:\u002F\u002Fobsproject.com\u002Fja\u002Fdownload",[203],"OBSのインストールはこちらから",[341,3135,3137],{"id":3136},"とりあえずmacの映像と音声を受信用rtmpに流そう","とりあえずmacの映像と音声を受信用RTMPに流そう",[18,3139,3140],{},"OBSをインストールしたらアプリを開きます。以下のような似た画面になっているはずです。（すでに設定がいくつかあるのは気にしないでください）",[85,3142],{":src":3143,":width":88},"'rtmp-docker-local\u002Fobs1.png'",[18,3145,3146],{},"「シーン」に何もなければ「＋」をクリックしてシーンを追加します。そして映像・音声ソースも追加していきます。「ソース」のとこの「＋」をクリックして「映像キャプチャデバイス」を選択します。",[18,3148,3149],{},"ソースのラベルを適当に入れておきます。",[85,3151],{":src":3152,":width":3153,":center":1445},"'rtmp-docker-local\u002Fobs2.png'","'400px'",[18,3155,3156],{},"ソースの選択画面に移動するので「デバイス」から「FaceTime HDカメラ」などを選択しておきます。",[85,3158],{":src":3159,":width":88},"'rtmp-docker-local\u002Fobs3.png'",[18,3161,3162],{},"これだとまだ映像しか拾わないので次は「ソース」のとこの「＋」をクリックして「音声入力キャプチャ」を選択します。同じようにソースのラベルを適当に入れておきます。ソースの選択画面に移動するので「デバイス」から「既定」などを選択しておきます。どれでもとりあえず音声が取得できればOKです。",[85,3164],{":src":3165,":width":88},"'rtmp-docker-local\u002Fobs4.png'",[18,3167,3168],{},"こんな感じで映像となんか奇声を発して「音声入力キャプチャ」が反応していればOKです。",[85,3170],{":src":3171,":width":88},"'rtmp-docker-local\u002Fobs5.png'",[18,3173,3174],{},"次に配信の設定をします。OBS→「設定」を開いて配信のタブをクリックします。\nサービスから「カスタム」を選択します。",[85,3176],{":src":3177,":width":88},"'rtmp-docker-local\u002Fobs6.png'",[18,3179,3180,3181,3184,3185,3188],{},"そして「サーバー」に受信用コンテナのRTMPパスをいれます。このとき",[136,3182,3183],{},"127.0.0.1","というローカルループバックアドレスにしておきます。localhostにするとうまくいかない時があります。自分は",[136,3186,3187],{},"localhost:1935","に受信用コンテナのエンドポイントが控えていますが、違うポートの時はそのポートを指定してください。「適用」をクリックして「OK」をクリックします。",[85,3190],{":src":3191,":width":88},"'rtmp-docker-local\u002Fobs7.png'",[18,3193,3194],{},"最初の画面にもどったら「配信開始」をクリックすると配信されます。下の方にこのような表示がされていたり、確認用playerで確認したり、HLSファイルが生成されていれば成功しています。",[85,3196],{":src":3197,":width":88},"'rtmp-docker-local\u002Fobs8.png'",[18,3199,3200],{},"終了する時は「配信終了」をクリックします。",[25,3202,3204],{"id":3203},"配信先にローカルの別のrtmpサーバを使用する","配信先にローカルの別のRTMPサーバを使用する。",[18,3206,3207],{},"最後に配信先のRTMPサーバをローカルに用意します。一番簡単な方法としてはもう1つRTMPサーバを立ててしまうことです。気をつける点としては管理用コンテナから配信先のホストを指定できるようにします。",[163,3209,3211],{"className":1703,"code":3210,"filename":160,"language":1705,"meta":171,"style":171},"version: '2'\nservices:\n  rtmptarget:\n    image: tiangolo\u002Fnginx-rtmp\n    ports:\n      - \"1930:1935\"\n      - \"1931:80\"\n    volumes:\n      - .\u002Fconf\u002Fnginx.conf:\u002Fetc\u002Fnginx\u002Fnginx.conf\n      - .\u002Fconf\u002Fdefault.conf:\u002Fetc\u002Fnginx\u002Fconf.d\u002Fdefault.conf\n      - .\u002Fdebug:\u002Fusr\u002Fshare\u002Fnginx\u002F\n    # ...\n  php:\n    # ...\n    extra_hosts:\n      - \"host.docker.internal:host-gateway\"\n    # ...\nvolumes:\n  shared_data:\n",[136,3212,3213,3225,3231,3237,3245,3251,3261,3271,3277,3283,3289,3295,3300,3306,3310,3316,3326,3330,3336],{"__ignoreMap":171},[182,3214,3215,3217,3219,3221,3223],{"class":184,"line":185},[182,3216,224],{"class":223},[182,3218,228],{"class":227},[182,3220,231],{"class":227},[182,3222,235],{"class":234},[182,3224,238],{"class":227},[182,3226,3227,3229],{"class":184,"line":191},[182,3228,243],{"class":223},[182,3230,246],{"class":227},[182,3232,3233,3235],{"class":184,"line":249},[182,3234,1730],{"class":223},[182,3236,246],{"class":227},[182,3238,3239,3241,3243],{"class":184,"line":257},[182,3240,1737],{"class":223},[182,3242,228],{"class":227},[182,3244,1742],{"class":234},[182,3246,3247,3249],{"class":184,"line":269},[182,3248,272],{"class":223},[182,3250,246],{"class":227},[182,3252,3253,3255,3257,3259],{"class":184,"line":277},[182,3254,280],{"class":227},[182,3256,283],{"class":227},[182,3258,1757],{"class":234},[182,3260,289],{"class":227},[182,3262,3263,3265,3267,3269],{"class":184,"line":292},[182,3264,280],{"class":227},[182,3266,283],{"class":227},[182,3268,1768],{"class":234},[182,3270,289],{"class":227},[182,3272,3273,3275],{"class":184,"line":304},[182,3274,307],{"class":223},[182,3276,246],{"class":227},[182,3278,3279,3281],{"class":184,"line":312},[182,3280,280],{"class":227},[182,3282,1783],{"class":234},[182,3284,3285,3287],{"class":184,"line":320},[182,3286,280],{"class":227},[182,3288,1790],{"class":234},[182,3290,3291,3293],{"class":184,"line":328},[182,3292,280],{"class":227},[182,3294,1797],{"class":234},[182,3296,3297],{"class":184,"line":411},[182,3298,3299],{"class":1068},"    # ...\n",[182,3301,3302,3304],{"class":184,"line":416},[182,3303,1852],{"class":223},[182,3305,246],{"class":227},[182,3307,3308],{"class":184,"line":422},[182,3309,3299],{"class":1068},[182,3311,3312,3314],{"class":184,"line":428},[182,3313,1932],{"class":223},[182,3315,246],{"class":227},[182,3317,3318,3320,3322,3324],{"class":184,"line":434},[182,3319,280],{"class":227},[182,3321,283],{"class":227},[182,3323,1943],{"class":234},[182,3325,289],{"class":227},[182,3327,3328],{"class":184,"line":440},[182,3329,3299],{"class":1068},[182,3331,3332,3334],{"class":184,"line":446},[182,3333,1950],{"class":223},[182,3335,246],{"class":227},[182,3337,3338,3340],{"class":184,"line":452},[182,3339,1957],{"class":223},[182,3341,246],{"class":227},[18,3343,3344,3345,3348,3349,3351],{},"管理用コンテナのプロパティに",[136,3346,3347],{},"extra_hosts","に",[136,3350,1943],{},"を加えることで、コンテナから利用できるホストマシンの特殊なIPアドレスがコンテナのhostsに追記されます。",[18,3353,3354,3357],{},[136,3355,3356],{},"cat \u002Fetc\u002Fhosts","を管理用サーバで実行すると追加したhostのIPがあるはずです。",[163,3359,3362],{"className":3360,"code":3361,"language":168},[166],"127.0.0.1       localhost\n::1     localhost ip6-localhost ip6-loopback\nfe00::0 ip6-localnet\nff00::0 ip6-mcastprefix\nff02::1 ip6-allnodes\nff02::2 ip6-allrouters\n192.168.65.2    host.docker.internal ←これ\n172.23.0.3      8227ce3421ec\n",[136,3363,3361],{"__ignoreMap":171},[45,3365,3366],{"id":3366},"ffmpegを実行する",[18,3368,3369],{},"次に配信先のRTMPコンテナに向けてffmpegを用いて配信します。ローカルのホスト、ポート、パスに合わせればいいだけですが以下のコマンドを使用します。（youtubeの時とすこし違います！！）",[163,3371,3374],{"className":3372,"code":3373,"language":168},[166],"ffmpeg -re -i \"\u002Fvar\u002Fwww\u002Fhtml\u002Fhls\u002F.m3u8\" -c:v libx264 -preset ultrafast -c:a aac -ar 44100 -ab 128k -ac 2 -f flv \"rtmp:\u002F\u002F192.168.65.2:1930\u002Flive\"\n",[136,3375,3373],{"__ignoreMap":171},[18,3377,3378,3379,3382],{},"このコマンドを打って配信先コンテナの確認用htmlのプレイヤー",[136,3380,3381],{},"http:\u002F\u002Flocalhost:1931\u002Findex.html","から映像を確認できれば受信できています。（20秒ほど待つといいです）",[18,3384,3385],{},"すこしオプションの内容がyoutubeのときと違っている理由として、単純にyoutubeのIPから変えればいいと思っていたらなぜかweb playerで映像が見れず音声しか流れなかったのが原因です。色々探ってこのコマンドにしたら問題なくいけました。もしかするとIPだけ変更してyoutubeもこのコマンドでやったらほうがいいかもしれません。検証中です。",[25,3387,3388],{"id":3388},"まとめと参考文献",[18,3390,3391],{},"記事は以上となります。一通り管理用の中継コンテナを用いてgoproの映像をffmpegでyoutubeに送信したり、ローカルの開発環境を整えました。次回はffmpegを用いて任意のシーンに切り替えたり、goproからの映像が途絶えた時などの例外処理を加えていくwebシステム部分をphpを使用して実装しようと思います。",[18,3393,3394],{},"今回参考にした資料です。",[32,3396,3397,3404],{},[35,3398,3399],{},[199,3400,3403],{"href":3401,"rel":3402},"https:\u002F\u002Fgist.github.com\u002Fqntmpkts\u002F403a027a4bfe99812ebf8952c41f8789",[203],"Restream m3u8 stream with ffmpeg to youtube live",[35,3405,3406],{},[199,3407,3410],{"href":3408,"rel":3409},"https:\u002F\u002Fqiita.com\u002FCyberRex\u002Fitems\u002F960bbd0f348ad8dca544",[203],"FFmpegでよく使う例、コーデックをまとめてみた（2023年版）",[1529,3412,1531],{},{"title":171,"searchDepth":249,"depth":249,"links":3414},[3415,3416,3417,3420,3425,3430,3433],{"id":1606,"depth":191,"text":1606},{"id":1629,"depth":191,"text":1629},{"id":2871,"depth":191,"text":2871,"children":3418},[3419],{"id":3003,"depth":249,"text":3003},{"id":3036,"depth":191,"text":3037,"children":3421},[3422,3423,3424],{"id":3043,"depth":249,"text":3044},{"id":3064,"depth":249,"text":3064},{"id":3082,"depth":249,"text":3083},{"id":3115,"depth":191,"text":3115,"children":3426},[3427],{"id":3121,"depth":249,"text":3122,"children":3428},[3429],{"id":3136,"depth":257,"text":3137},{"id":3203,"depth":191,"text":3204,"children":3431},[3432],{"id":3366,"depth":249,"text":3366},{"id":3388,"depth":191,"text":3388},[1560],"2024-07-09","youtbeへの配信テストとデバッグ環境構築",{},"\u002Fseries\u002Frtmp-manager-server-2",{"title":1578,"description":3436},"series\u002Frtmp-manager-server-2",[1571,1572],"rtmp-docker-local\u002Fobs5.png","HPFKEu4iBy3O40z_uOHtY7Ed_jACJ4aBXKcnLMlAOLo",1780987150046]