リモート参加者と物理イベント会場をつなぐハイブリットイベントツール"Cisco Webex Physical"

#はじめに
この記事はシスコの有志による Cisco Systems Japan Advent Calendar 2021 (2枚目）の 4日目です。
2017年版: https://qiita.com/advent-calendar/2017/cisco
2018年版: https://qiita.com/advent-calendar/2018/cisco
2019年版: https://qiita.com/advent-calendar/2019/cisco
2020年版 1枚目: https://qiita.com/advent-calendar/2020/cisco
2020年版 2枚目: https://qiita.com/advent-calendar/2020/cisco2
2021年版 1枚目: https://qiita.com/advent-calendar/2021/cisco
2021年版 2枚目: https://qiita.com/advent-calendar/2021/cisco2 <---こちら

本記事は、以下二つの記事の続編となります。

https://qiita.com/hiokazak/items/9dc63725fe9000256e88

https://qiita.com/ayamazak/items/ebaafcf6e71c77f4291f
#ハイブリットイベントの現状と課題
Under COVID-19環境では、物理とリモート参加者が一緒に参加するハイブリッドイベントが行われるようになりましたが、リモートから参加すると物理会場の様子が分からず、企業ブースや講演会場に入りづらいと感じたことはありませんか？
気になる企業ブースを見つけて、リモートからブースに入ったら、参加者が自分1名だけ！立ち寄るだけのつもりが長話になってしまった！なんて経験をお持ちの方もいらっしゃるのではないでしょうか。
リモート参加者は、様子が分からない物理会場との間に壁を感じてしまい、参加者数に影響が出ているイベントもあるようです。
#やりたいこと
お客様から｢イベント集客が見込めないので、企業がブースを出展してくれず、イベントを企画する予算を確保するのが難しい。シスコのソリューションで解決できませんか？｣というご相談を受けました。ハイブリッドイベントの現状であるリモートと物理会場の壁を無くし、リモート参加者を増やすために、シスコのエンジニア有志3名でハイブリットイベントツール（通称”Cisco　Webex　Physical”）を作成しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324859/688d77cb-5274-b545-f546-c77df78411de.png)
#解決したいポイント
既存のバーチャルイベントツールでは対応が難しいポイントをまとめました。
今回はMerakiカメラとWebex Meetingsを組み合わせて解決していきたいと思います。
![スクリーンショット 2021-12-22 4.18.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324859/b0e3e4ad-45fc-fead-47c0-b8d3f564cd95.png)
(1) リモートからの参加者のみを前提に作られている　（物理イベント会場の情報が分からない）
(2) 気軽に話せない/話しかけられない
(3) バーチャルセッションルームに入らないと、どんな会話をしているのか分からない。
(4) 事前にブースの様子が分からないので、バーチャルセッションルームへの入室をためらってしまう

#仕組み
![スクリーンショット 2021-12-22 1.05.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324859/0d822584-6b78-97ba-1fb4-ac4a401184f5.png)
**Step1: リモート参加者用のバーチャルイベントサイトを作成**
サイト内を散策し、物理会場内のセミナーや企業ブースの様子を収集できる
→リモートでも物理イベント会場の雰囲気が分かる

**Step2: ブースの上をマウスオーバーすると、ブース内の情報が表示される**
ブースにいる物理/リモート参加者の人数、ブースのリアルタイム映像を見れる
リアルタイム映像はMerakiカメラからExternal RTSPを使って転送し、人数のカウントは[Meraki MV Sense API](https://gblogs.cisco.com/jp/2021/02/avoid-the-3cs-via-cisco-meraki-smart-camera/)を使用
→ブースに入らなくても、動画で状況把握し、参加人数を確認できる。その結果ブースに入りやすくなる。
![スクリーンショット 2021-12-22 1.22.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324859/a31f59a5-f0f2-34f7-b43b-f17dc414c314.png)
**Step3: ブースをクリックするとSlidoが表示。匿名でチャットできる**
ブースをクリックすると、Slidoが表示され、物理会場にいるメンバーと匿名で質問/会話ができる
→気軽に話せる

**Step4: Enter the BoothをクリックするとWebex Meetingが開始される**
直接会話がしたくなったら、クリック一つで物理会場にいる担当者とWebexで会話
→シームレスにWebex Meetingsを開始できる

#解説&デモ動画

https://www.youtube.com/watch?v=sLm0GvPa56w

#実行コード
**Pythonスクリプト**
Merakiカメラのリアルタイム映像（video_feed）を取得

```python.py
from flask import Flask, render_template, Response
from flask import request
import cv2
import requests
from detect import personcount
from webexbg import get_from_ini, read_allparticipants

app = Flask(__name__)

meraki_api_key = ""
cam_serial = ""

headers = {
  'Authorization': 'Basic ' + get_from_ini("my_token_xapi"),
  'Content-Type': 'text/xml'
}
endpoint_ip = get_from_ini('endpoint_ip')

def get_rtspurl(cam_serial):
    headers = {
        "Content-Type": "application/json",
        "Accept": "application/json",
        "X-Cisco-Meraki-API-Key": meraki_api_key
    }

    try:
        r_rtspurl = requests.request('GET', f"https://api.meraki.com/api/v1/devices/{cam_serial}/camera/video/settings", headers=headers)
        r_rtspurl_json = r_rtspurl.json()
        return r_rtspurl_json["rtspUrl"]
    except Exception as e:
        return print(f"Error when getting image URL: {e}") 

camera = cv2.VideoCapture(get_rtspurl(cam_serial))

def gen_frames():
   while True:
       success, frame = camera.read()
       if not success:
           break
       else:
           ret, buffer = cv2.imencode('.jpg',frame)
           frame = buffer.tobytes()
           yield (b'--frame\r\n'
                  b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')


@app.route('/video_feed')
def video_feed():
   return Response(gen_frames(), mimetype='multipart/x-mixed-replace; boundary=frame')

if __name__ == "__main__":
    app.run(threaded=True)
```


**リモート参加者用のバーチャルイベントサイト**
各ブースのリアルタイム映像(video_feed)や人数(Meraki MV Sence API)等を表示

```html
<html>

    <head>
    <title>Webex Phisical</title>
    <meta charset="UTF-8">
    <script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/jQuery-rwdImageMaps/1.6/jquery.rwdImageMaps.min.js"></script>
    <script src="static/js/main.js"></script>
    
    <style>
        #preview{
            position:absolute;
            border:1px solid #ccc;
            background:#333;
            padding:5px;
            display:none;
            color:#fff;
            }
    </style>

    <script>
    
    function changeMapImage(imgPath) {
      document.getElementById('map').src = imgPath;
    }
    
    function location_click(){
        var objElement = document.getElementById( 'map' );
        objElement.src = 'href';
        return false;
    }
    
    jQuery(document).ready(function(e) {
        jQuery('img[usemap]').rwdImageMaps();
    
        jQuery('img#map').on('click', function() {
            //alert($(this).attr('href') + ' clicked');
            //window.location.href = '#';
            //location_click($(this).attr('href'));
            //location.assign.getAttribute'$(this).attr("href")'
            return false;
        });
        
        jQuery('area').on('click', function() {
            //alert($(this).attr('href') + ' clicked');
            window.location.href = $(this).attr('href');
            //location_click($(this).attr('href'));
            //location.assign.getAttribute'$(this).attr("href")'
            return false;
        });
    });
    </script>
    </head>
    <body>
    <h1>Webex Physical Map</h1>

    <img src="static/images/main.png" usemap="#ImageMap" alt="" style="cursor: default;" /><map name="ImageMap">
                            <a href="./video_feed" class="preview" title="Medical Company<br>Onsite: {{people}} people<br>Online: {{webex=webex}} people"><area shape="rect" coords="130,754,400,909" href="./company_booth" alt="Medical Company Booth" /></a>
                            
    
    </map>
    </body>
    </html>
```

**ブースをクリックすると表示されるWEB画面**
リアルタイム映像に加えて、Slidoを表示し、Webex Meetingsへのリンクを追加

```html
<html>
<head>
<title>Company Booth Page</title>
<meta charset="UTF-8">
</head>
<body>
<p>
<img swidth="90" height="120" src="./static/images/logo.png" alt="logo" align="middle">
<font size="6" color="#0000ff">Welcome to Medical Company Booth !!</font>
</p>
<h2> Streaming Video at Physical Booth</h2>
<img src="{{ url_for('video_feed') }}" width='1440' height='810'>
<h3><a href="https://cisco.webex.com/cisco-jp/j.php?MTID=m2b51e7d8adbdbff9dd2d72c8a21e7786"> Click here to Enter the Booth. {{people}} people are here.</a></h3>
<h2> If you have any questions, please feel free to contact us from the following! </h2>
<iframe src="https://app.sli.do/event/pwanzvyo/questions" height="100%" width="100%" frameBorder="0" style="min-height: 560px;" title="Slido"></iframe>
</body>
</html>
```




    
#終わりに
今回はMerakiカメラからのリアルタイム映像のみを表示させましたが、同時に音声も流すなどの応用もできそうです。DevNet Code ExchangeとGitHubにも情報を掲載していますので、よろしければご活用ください。

* DevNet Code Exchange

https://developer.cisco.com/codeexchange/github/repo/ayamazak/Webex_Physical

* GitHub

https://github.com/ayamazak/Webex_Physical

#免責事項
本サイトおよび対応するコメントにおいて表明される意見は、投稿者本人の個人的意見であり、シスコの意見ではありません。本サイトの内容は、情報の提供のみを目的として掲載されており、シスコや他の関係者による推奨や表明を目的としたものではありません。各利用者は、本Webサイトへの掲載により、投稿、リンクその他の方法でアップロードした全ての情報の内容に対して全責任を負い、本Web サイトの利用に関するあらゆる責任からシスコを免責することに同意したものとします。