Recherche avancée

Médias (1)

Mot : - Tags -/copyleft

Autres articles (54)

  • Creating farms of unique websites

    13 avril 2011, par

    MediaSPIP platforms can be installed as a farm, with a single "core" hosted on a dedicated server and used by multiple websites.
    This allows (among other things) : implementation costs to be shared between several different projects / individuals rapid deployment of multiple unique sites creation of groups of like-minded sites, making it possible to browse media in a more controlled and selective environment than the major "open" (...)

  • Publier sur MédiaSpip

    13 juin 2013

    Puis-je poster des contenus à partir d’une tablette Ipad ?
    Oui, si votre Médiaspip installé est à la version 0.2 ou supérieure. Contacter au besoin l’administrateur de votre MédiaSpip pour le savoir

  • XMP PHP

    13 mai 2011, par

    Dixit Wikipedia, XMP signifie :
    Extensible Metadata Platform ou XMP est un format de métadonnées basé sur XML utilisé dans les applications PDF, de photographie et de graphisme. Il a été lancé par Adobe Systems en avril 2001 en étant intégré à la version 5.0 d’Adobe Acrobat.
    Étant basé sur XML, il gère un ensemble de tags dynamiques pour l’utilisation dans le cadre du Web sémantique.
    XMP permet d’enregistrer sous forme d’un document XML des informations relatives à un fichier : titre, auteur, historique (...)

Sur d’autres sites (9538)

  • Evolution #4271 (Nouveau) : valider_url_distante => pouvoir déclarer des domaines distants et pas ...

    21 janvier 2019, par - Equipement

    Bonjour,

    La fonction valider_url_distante permet de déclarer des hosts distants :

    1. <span class="CodeRay"><span class="local-variable">$known_hosts</span> = pipeline(<span class="string"><span class="delimiter">'</span><span class="content">declarer_hosts_distants</span><span class="delimiter">'</span></span>, <span class="local-variable">$known_hosts</span>);
    2. </span>

    Télécharger

    C’est très bien, mais avec plusieurs centaines de hosts à déclarer (dont la liste varie dans le temps), cela devient compliqué à gérer.

    Serait-il possible d’avoir, en plus, la possibilité de déclarer des domaines distants ?

    Une première piste serait de pouvoir déclarer .example.com dans le pipeline pour inclure tous les *.example.com au lieu des les énumérer un par un.

    Une seconde piste consisterait à s’inspirer de la function need_proxy (via une constante avec la même syntaxe que pour le http_noproxy) :

    // Pour mémoire code actuel à conserver ...

    1. <span class="CodeRay">    <span class="local-variable">$is_known_host</span> = <span class="predefined-constant">false</span>;
    2.     <span class="keyword">foreach</span> (<span class="local-variable">$known_hosts</span> <span class="keyword">as</span> <span class="local-variable">$known_host</span>) {
    3.         <span class="local-variable">$parse_known</span> = <span class="predefined">parse_url</span>(<span class="local-variable">$known_host</span>);
    4.         <span class="keyword">if</span> (<span class="local-variable">$parse_known</span>
    5.           <span class="keyword">and</span> <span class="predefined">strtolower</span>(<span class="local-variable">$parse_known</span>[<span class="string"><span class="delimiter">'</span><span class="content">host</span><span class="delimiter">'</span></span>]) === <span class="predefined">strtolower</span>(<span class="local-variable">$parsed_url</span>[<span class="string"><span class="delimiter">'</span><span class="content">host</span><span class="delimiter">'</span></span>])) {
    6.             <span class="local-variable">$is_known_host</span> = <span class="predefined-constant">true</span>;
    7.             <span class="keyword">break</span>;
    8.         }
    9.     }
    10. </span>

    Télécharger

    // ... que l’on pourrait faire suivre de ce code (complètement inspiré de function need_proxy) :

    1. <span class="CodeRay">    <span class="keyword">if</span> (!<span class="local-variable">$is_known_host</span>) {
    2.                 <span class="local-variable">$known_domaines</span> = _DECLARER_DOMAINES_DISTANTS;
    3.  
    4.                 <span class="local-variable">$known_domaines</span> = <span class="predefined">str_replace</span>(<span class="string"><span class="delimiter">"</span><span class="char">\n</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content"> </span><span class="delimiter">"</span></span>, <span class="local-variable">$known_domaines</span>);
    5.                 <span class="local-variable">$known_domaines</span> = <span class="predefined">str_replace</span>(<span class="string"><span class="delimiter">"</span><span class="char">\r</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content"> </span><span class="delimiter">"</span></span>, <span class="local-variable">$known_domaines</span>);
    6.                 <span class="local-variable">$known_domaines</span> = <span class="string"><span class="delimiter">"</span><span class="content"> </span><span class="local-variable">$known_domaines</span><span class="content"> </span><span class="delimiter">"</span></span>;
    7.                 <span class="local-variable">$domain</span> = <span class="predefined">strtolower</span>(<span class="local-variable">$parsed_url</span>[<span class="string"><span class="delimiter">'</span><span class="content">host</span><span class="delimiter">'</span></span>]);
    8.  
    9.                 <span class="keyword">if</span> (<span class="predefined">strpos</span>(<span class="local-variable">$known_domaines</span>, <span class="string"><span class="delimiter">"</span><span class="content"> </span><span class="local-variable">$domain</span><span class="content"> </span><span class="delimiter">"</span></span>) !== <span class="predefined-constant">false</span>){
    10.                     <span class="local-variable">$is_known_host</span> = <span class="predefined-constant">true</span>;
    11.                 }
    12.  
    13.                 <span class="keyword">while</span> (<span class="predefined">strpos</span>(<span class="local-variable">$domain</span>, <span class="string"><span class="delimiter">'</span><span class="content">.</span><span class="delimiter">'</span></span>) !== <span class="predefined-constant">false</span>) {
    14.                         <span class="local-variable">$domain</span> = <span class="predefined">explode</span>(<span class="string"><span class="delimiter">'</span><span class="content">.</span><span class="delimiter">'</span></span>, <span class="local-variable">$domain</span>);
    15.                         <span class="predefined">array_shift</span>(<span class="local-variable">$domain</span>);
    16.                         <span class="local-variable">$domain</span> = <span class="predefined">implode</span>(<span class="string"><span class="delimiter">'</span><span class="content">.</span><span class="delimiter">'</span></span>, <span class="local-variable">$domain</span>);
    17.  
    18.                         <span class="keyword">if</span> (<span class="predefined">strpos</span>(<span class="local-variable">$known_domaines</span>, <span class="string"><span class="delimiter">"</span><span class="content"> .</span><span class="local-variable">$domain</span><span class="content"> </span><span class="delimiter">"</span></span>) !== <span class="predefined-constant">false</span>) {
    19.                             <span class="local-variable">$is_known_host</span> = <span class="predefined-constant">true</span>;
    20.                         }
    21.                 }
    22.         }
    23. </span>

    Télécharger

    Cordialement
    Equipement

  • record mediasoup RTP stream using FFmpeg for Firefox

    30 juillet 2024, par Hadi Aghandeh

    I am trying to record WebRTC stream using mediasoup. I could record successfully on chrome and safari 13/14/15. However on Firefox the does not work.

    &#xA;

    Client side code is a vue js component which gets rtp-compabilities using socket.io and create producers after the server creates the transports. This works good on chrome and safari.

    &#xA;

    const { connect , createLocalTracks } = require(&#x27;twilio-video&#x27;);&#xA;const SocketClient = require("socket.io-client");&#xA;const SocketPromise = require("socket.io-promise").default;&#xA;const MediasoupClient = require("mediasoup-client");&#xA;&#xA;export default {&#xA;    data() {&#xA;        return {&#xA;            errors: [],&#xA;            isReady: false,&#xA;            isRecording: false,&#xA;            loading: false,&#xA;            sapio: {&#xA;                token: null,&#xA;                connectionId: 0&#xA;            },&#xA;            server: {&#xA;                host: &#x27;https://rtc.test&#x27;,&#xA;                ws: &#x27;/server&#x27;,&#xA;                socket: null,&#xA;            },&#xA;            peer: {},&#xA;        }&#xA;    },&#xA;    mounted() {&#xA;        this.init();&#xA;    },&#xA;    methods: {&#xA;        async init() {&#xA;            await this.startCamera();&#xA;&#xA;            if (this.takeId) {&#xA;                await this.recordBySapioServer();&#xA;            }&#xA;        },&#xA;        startCamera() {&#xA;            return new Promise( (resolve, reject) => {&#xA;                if (window.videoMediaStreamObject) {&#xA;                    this.setVideoElementStream(window.videoMediaStreamObject);&#xA;                    resolve();&#xA;                } else {&#xA;                    // Get user media as required&#xA;                    try {&#xA;                        this.localeStream = navigator.mediaDevices.getUserMedia({&#xA;                            audio: true,&#xA;                            video: true,&#xA;                        }).then((stream) => {&#xA;                            this.setVideoElementStream(stream);&#xA;                            resolve();&#xA;                        })&#xA;                    } catch (err) {&#xA;                        console.error(err);&#xA;                        reject();&#xA;                    }&#xA;                }&#xA;            })&#xA;        },&#xA;        setVideoElementStream(stream) {&#xA;            this.localStream = stream;&#xA;            this.$refs.video.srcObject = stream;&#xA;            this.$refs.video.muted = true;&#xA;            this.$refs.video.play().then((video) => {&#xA;                this.isStreaming = true;&#xA;                this.height = this.$refs.video.videoHeight;&#xA;                this.width = this.$refs.video.videoWidth;&#xA;            });&#xA;        },&#xA;        // first thing we need is connecting to websocket&#xA;        connectToSocket() {&#xA;            const serverUrl = this.server.host;&#xA;            console.log("Connect with sapio rtc server:", serverUrl);&#xA;&#xA;            const socket = SocketClient(serverUrl, {&#xA;                path:  this.server.ws,&#xA;                transports: ["websocket"],&#xA;            });&#xA;            this.socket = socket;&#xA;&#xA;            socket.on("connect", () => {&#xA;                console.log("WebSocket connected");&#xA;                // we ask for rtp-capabilities from server to send to us&#xA;                socket.emit(&#x27;send-rtp-capabilities&#x27;);&#xA;            });&#xA;&#xA;            socket.on("error", (err) => {&#xA;                this.loading = true;&#xA;                console.error("WebSocket error:", err);&#xA;            });&#xA;&#xA;            socket.on("router-rtp-capabilities", async (msg) => {&#xA;                const { routerRtpCapabilities, sessionId, externalId } = msg;&#xA;                console.log(&#x27;[rtpCapabilities:%o]&#x27;, routerRtpCapabilities);&#xA;                this.routerRtpCapabilities = routerRtpCapabilities;&#xA;&#xA;                try {&#xA;                    const device = new MediasoupClient.Device();&#xA;                    // Load the mediasoup device with the router rtp capabilities gotten from the server&#xA;                    await device.load({ routerRtpCapabilities });&#xA;&#xA;                    this.peer.sessionId = sessionId;&#xA;                    this.peer.externalId = externalId;&#xA;                    this.peer.device = device;&#xA;&#xA;                    this.createTransport();&#xA;                } catch (error) {&#xA;                    console.error(&#x27;failed to init device [error:%o]&#x27;, error);&#xA;                    socket.disconnect();&#xA;                }&#xA;            });&#xA;&#xA;            socket.on("create-transport", async (msg) => {&#xA;                console.log(&#x27;handleCreateTransportRequest() [data:%o]&#x27;, msg);&#xA;&#xA;                try {&#xA;                    // Create the local mediasoup send transport&#xA;                    this.peer.sendTransport = await this.peer.device.createSendTransport(msg);&#xA;                    console.log(&#x27;send transport created [id:%s]&#x27;, this.peer.sendTransport.id);&#xA;&#xA;                    // Set the transport listeners and get the users media stream&#xA;                    this.handleSendTransportListeners();&#xA;                    this.setTracks();&#xA;                    this.loading = false;&#xA;                } catch (error) {&#xA;                    console.error(&#x27;failed to create transport [error:%o]&#x27;, error);&#xA;                    socket.disconnect();&#xA;                }&#xA;            });&#xA;&#xA;            socket.on("connect-transport", async (msg) => {&#xA;                console.log(&#x27;handleTransportConnectRequest()&#x27;);&#xA;                try {&#xA;                    const action = this.connectTransport;&#xA;&#xA;                    if (!action) {&#xA;                        throw new Error(&#x27;transport-connect action was not found&#x27;);&#xA;                    }&#xA;&#xA;                    await action(msg);&#xA;                } catch (error) {&#xA;                    console.error(&#x27;ailed [error:%o]&#x27;, error);&#xA;                }&#xA;            });&#xA;&#xA;            socket.on("produce", async (msg) => {&#xA;                console.log(&#x27;handleProduceRequest()&#x27;);&#xA;                try {&#xA;                    if (!this.produce) {&#xA;                        throw new Error(&#x27;produce action was not found&#x27;);&#xA;                    }&#xA;                    await this.produce(msg);&#xA;                } catch (error) {&#xA;                    console.error(&#x27;failed [error:%o]&#x27;, error);&#xA;                }&#xA;            });&#xA;&#xA;            socket.on("recording", async (msg) => {&#xA;                this.isRecording = true;&#xA;            });&#xA;&#xA;            socket.on("recording-error", async (msg) => {&#xA;                this.isRecording = false;&#xA;                console.error(msg);&#xA;            });&#xA;&#xA;            socket.on("recording-closed", async (msg) => {&#xA;                this.isRecording = false;&#xA;                console.warn(msg)&#xA;            });&#xA;&#xA;        },&#xA;        createTransport() {&#xA;            console.log(&#x27;createTransport()&#x27;);&#xA;&#xA;            if (!this.peer || !this.peer.device.loaded) {&#xA;                throw new Error(&#x27;Peer or device is not initialized&#x27;);&#xA;            }&#xA;&#xA;            // First we must create the mediasoup transport on the server side&#xA;            this.socket.emit(&#x27;create-transport&#x27;,{&#xA;                sessionId: this.peer.sessionId&#xA;            });&#xA;        },&#xA;        handleSendTransportListeners() {&#xA;            this.peer.sendTransport.on(&#x27;connect&#x27;, this.handleTransportConnectEvent);&#xA;            this.peer.sendTransport.on(&#x27;produce&#x27;, this.handleTransportProduceEvent);&#xA;            this.peer.sendTransport.on(&#x27;connectionstatechange&#x27;, connectionState => {&#xA;                console.log(&#x27;send transport connection state change [state:%s]&#x27;, connectionState);&#xA;            });&#xA;        },&#xA;        handleTransportConnectEvent({ dtlsParameters }, callback, errback) {&#xA;            console.log(&#x27;handleTransportConnectEvent()&#x27;);&#xA;            try {&#xA;                this.connectTransport = (msg) => {&#xA;                    console.log(&#x27;connect-transport action&#x27;);&#xA;                    callback();&#xA;                    this.connectTransport = null;&#xA;                };&#xA;&#xA;                this.socket.emit(&#x27;connect-transport&#x27;,{&#xA;                    sessionId: this.peer.sessionId,&#xA;                    transportId: this.peer.sendTransport.id,&#xA;                    dtlsParameters&#xA;                });&#xA;&#xA;            } catch (error) {&#xA;                console.error(&#x27;handleTransportConnectEvent() failed [error:%o]&#x27;, error);&#xA;                errback(error);&#xA;            }&#xA;        },&#xA;        handleTransportProduceEvent({ kind, rtpParameters }, callback, errback)  {&#xA;            console.log(&#x27;handleTransportProduceEvent()&#x27;);&#xA;            try {&#xA;                this.produce = jsonMessage => {&#xA;                    console.log(&#x27;handleTransportProduceEvent callback [data:%o]&#x27;, jsonMessage);&#xA;                    callback({ id: jsonMessage.id });&#xA;                    this.produce = null;&#xA;                };&#xA;&#xA;                this.socket.emit(&#x27;produce&#x27;, {&#xA;                    sessionId: this.peer.sessionId,&#xA;                    transportId: this.peer.sendTransport.id,&#xA;                    kind,&#xA;                    rtpParameters&#xA;                });&#xA;            } catch (error) {&#xA;                console.error(&#x27;handleTransportProduceEvent() failed [error:%o]&#x27;, error);&#xA;                errback(error);&#xA;            }&#xA;        },&#xA;        async recordBySapioServer() {&#xA;            this.loading = true;&#xA;            this.connectToSocket();&#xA;        },&#xA;        async setTracks() {&#xA;            // Start mediasoup-client&#x27;s WebRTC producers&#xA;            const audioTrack = this.localStream.getAudioTracks()[0];&#xA;            this.peer.audioProducer = await this.peer.sendTransport.produce({&#xA;                track: audioTrack,&#xA;                codecOptions :&#xA;                    {&#xA;                        opusStereo : 1,&#xA;                        opusDtx    : 1&#xA;                    }&#xA;            });&#xA;&#xA;&#xA;            let encodings;&#xA;            let codec;&#xA;            const codecOptions = {videoGoogleStartBitrate : 1000};&#xA;&#xA;            codec = this.peer.device.rtpCapabilities.codecs.find((c) => c.kind.toLowerCase() === &#x27;video&#x27;);&#xA;            if (codec.mimeType.toLowerCase() === &#x27;video/vp9&#x27;) {&#xA;                encodings = { scalabilityMode: &#x27;S3T3_KEY&#x27; };&#xA;            } else {&#xA;                encodings = [&#xA;                    { scaleResolutionDownBy: 4, maxBitrate: 500000 },&#xA;                    { scaleResolutionDownBy: 2, maxBitrate: 1000000 },&#xA;                    { scaleResolutionDownBy: 1, maxBitrate: 5000000 }&#xA;                ];&#xA;            }&#xA;            const videoTrack = this.localStream.getVideoTracks()[0];&#xA;            this.peer.videoProducer =await this.peer.sendTransport.produce({&#xA;                track: videoTrack,&#xA;                encodings,&#xA;                codecOptions,&#xA;                codec&#xA;            });&#xA;&#xA;        },&#xA;        startRecording() {&#xA;            this.Q.answer.recordingId = this.peer.externalId;&#xA;            this.socket.emit("start-record", {&#xA;                sessionId: this.peer.sessionId&#xA;            });&#xA;        },&#xA;        stopRecording() {&#xA;            this.socket.emit("stop-record" , {&#xA;                sessionId: this.peer.sessionId&#xA;            });&#xA;        },&#xA;    },&#xA;&#xA;}&#xA;&#xA;&#xA;&#xA;

    &#xA;

    console.log of my ffmpeg process :

    &#xA;

    // sdp string&#xA;[sdpString:v=0&#xA;  o=- 0 0 IN IP4 127.0.0.1&#xA;  s=FFmpeg&#xA;  c=IN IP4 127.0.0.1&#xA;  t=0 0&#xA;  m=video 25549 RTP/AVP 101 &#xA;  a=rtpmap:101 VP8/90000&#xA;  a=sendonly&#xA;  m=audio 26934 RTP/AVP 100 &#xA;  a=rtpmap:100 opus/48000/2&#xA;  a=sendonly&#xA;  ]&#xA;&#xA;// ffmpeg args&#xA;commandArgs:[&#xA;  &#x27;-loglevel&#x27;,&#xA;  &#x27;debug&#x27;,&#xA;  &#x27;-protocol_whitelist&#x27;,&#xA;  &#x27;pipe,udp,rtp&#x27;,&#xA;  &#x27;-fflags&#x27;,&#xA;  &#x27;&#x2B;genpts&#x27;,&#xA;  &#x27;-f&#x27;,&#xA;  &#x27;sdp&#x27;,&#xA;  &#x27;-i&#x27;,&#xA;  &#x27;pipe:0&#x27;,&#xA;  &#x27;-map&#x27;,&#xA;  &#x27;0:v:0&#x27;,&#xA;  &#x27;-c:v&#x27;,&#xA;  &#x27;copy&#x27;,&#xA;  &#x27;-map&#x27;,&#xA;  &#x27;0:a:0&#x27;,&#xA;  &#x27;-strict&#x27;,&#xA;  &#x27;-2&#x27;,&#xA;  &#x27;-c:a&#x27;,&#xA;  &#x27;copy&#x27;,&#xA;  &#x27;-f&#x27;,&#xA;  &#x27;webm&#x27;,&#xA;  &#x27;-flags&#x27;,&#xA;  &#x27;&#x2B;global_header&#x27;,&#xA;  &#x27;-y&#x27;,&#xA;  &#x27;storage/recordings/26e63cb3-4f81-499e-941a-c0bb7f7f52ce.webm&#x27;,&#xA;  [length]: 26&#xA;]&#xA;// ffmpeg log&#xA;ffmpeg::process::data [data:&#x27;ffmpeg version n4.4&#x27;]&#xA;ffmpeg::process::data [data:&#x27; Copyright (c) 2000-2021 the FFmpeg developers&#x27;]&#xA;ffmpeg::process::data [data:&#x27;\n&#x27;]&#xA;ffmpeg::process::data [data:&#x27;  built with gcc 11.1.0 (GCC)\n&#x27;]&#xA;ffmpeg::process::data [data:&#x27;  configuration: --prefix=/usr --disable-debug --disable-static --disable-stripping --enable-amf --enable-avisynth --enable-cuda-llvm --enable-lto --enable-fontconfig --enable-gmp --enable-gnutls --enable-gpl --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libdav1d --enable-libdrm --enable-libfreetype --enable-libfribidi --enable-libgsm --enable-libiec61883 --enable-libjack --enable-libmfx --enable-libmodplug --enable-libmp3lame --enable-libopencore_amrnb --enable-libopencore_amrwb --enable-libopenjpeg --enable-libopus --enable-libpulse --enable-librav1e --enable-librsvg --enable-libsoxr --enable-libspeex --enable-libsrt --enable-libssh --enable-libsvtav1 --enable-libtheora --enable-libv4l2 --enable-libvidstab --enable-libvmaf --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxcb --enable-libxml2 --enable-libxvid --enable-libzimg --enable-nvdec --enable-nvenc --enable-shared --enable-version3\n&#x27;]&#xA;ffmpeg::process::data [data:&#x27;  libavutil      56. 70.100 / 56. 70.100\n&#x27; &#x2B;&#xA;  &#x27;  libavcodec     58.134.100 / 58.134.100\n&#x27; &#x2B;&#xA;  &#x27;  libavformat    58. 76.100 / 58. 76.100\n&#x27; &#x2B;&#xA;  &#x27;  libavdevice    58. 13.100 / 58. 13.100\n&#x27; &#x2B;&#xA;  &#x27;  libavfilter     7.110.100 /  7.110.100\n&#x27; &#x2B;&#xA;  &#x27;  libswscale      5.  9.100 /  5.  9.100\n&#x27; &#x2B;&#xA;  &#x27;  libswresample   3.  9.100 /  3.  9.100\n&#x27; &#x2B;&#xA;  &#x27;  libpostproc    55.  9.100 / 55.  9.100\n&#x27; &#x2B;&#xA;  &#x27;Splitting the commandline.\n&#x27; &#x2B;&#xA;  "Reading option &#x27;-loglevel&#x27; ... matched as option &#x27;loglevel&#x27; (set logging level) with argument &#x27;debug&#x27;.\n" &#x2B;&#xA;  "Reading option &#x27;-protocol_whitelist&#x27; ..."]&#xA;ffmpeg::process::data [data:" matched as AVOption &#x27;protocol_whitelist&#x27; with argument &#x27;pipe,udp,rtp&#x27;.\n" &#x2B;&#xA;  "Reading option &#x27;-fflags&#x27; ..."]&#xA;ffmpeg::process::data [data:" matched as AVOption &#x27;fflags&#x27; with argument &#x27;&#x2B;genpts&#x27;.\n" &#x2B;&#xA;  "Reading option &#x27;-f&#x27; ... matched as option &#x27;f&#x27; (force format) with argument &#x27;sdp&#x27;.\n" &#x2B;&#xA;  "Reading option &#x27;-i&#x27; ... matched as input url with argument &#x27;pipe:0&#x27;.\n" &#x2B;&#xA;  "Reading option &#x27;-map&#x27; ... matched as option &#x27;map&#x27; (set input stream mapping) with argument &#x27;0:v:0&#x27;.\n" &#x2B;&#xA;  "Reading option &#x27;-c:v&#x27; ... matched as option &#x27;c&#x27; (codec name) with argument &#x27;copy&#x27;.\n" &#x2B;&#xA;  "Reading option &#x27;-map&#x27; ... matched as option &#x27;map&#x27; (set input stream mapping) with argument &#x27;0:a:0&#x27;.\n" &#x2B;&#xA;  "Reading option &#x27;-strict&#x27; ...Routing option strict to both codec and muxer layer\n" &#x2B;&#xA;  " matched as AVOption &#x27;strict&#x27; with argument &#x27;-2&#x27;.\n" &#x2B;&#xA;  "Reading option &#x27;-c:a&#x27; ... matched as option &#x27;c&#x27; (codec name) with argument &#x27;copy&#x27;.\n" &#x2B;&#xA;  "Reading option &#x27;-f&#x27; ... matched as option &#x27;f&#x27; (force format) with argument &#x27;webm&#x27;.\n" &#x2B;&#xA;  "Reading option &#x27;-flags&#x27; ... matched as AVOption &#x27;flags&#x27; with argument &#x27;&#x2B;global_header&#x27;.\n" &#x2B;&#xA;  "Reading option &#x27;-y&#x27; ... matched as option &#x27;y&#x27; (overwrite output files) with argument &#x27;1&#x27;.\n" &#x2B;&#xA;  "Reading option &#x27;storage/recordings/26e63cb3-4f81-499e-941a-c0bb7f7f52ce.webm&#x27; ... matched as output url.\n" &#x2B;&#xA;  &#x27;Finished splitting the commandline.\n&#x27; &#x2B;&#xA;  &#x27;Parsing a group of options: global .\n&#x27; &#x2B;&#xA;  &#x27;Applying option loglevel (set logging level) with argument debug.\n&#x27; &#x2B;&#xA;  &#x27;Applying option y (overwrite output files) with argument 1.\n&#x27; &#x2B;&#xA;  &#x27;Successfully parsed a group of options.\n&#x27; &#x2B;&#xA;  &#x27;Parsing a group of options: input url pipe:0.\n&#x27; &#x2B;&#xA;  &#x27;Applying option f (force format) with argument sdp.\n&#x27; &#x2B;&#xA;  &#x27;Successfully parsed a group of options.\n&#x27; &#x2B;&#xA;  &#x27;Opening an input file: pipe:0.\n&#x27; &#x2B;&#xA;  "[sdp @ 0x55604dc58400] Opening &#x27;pipe:0&#x27; for reading\n" &#x2B;&#xA;  &#x27;[sdp @ 0x55604dc58400] video codec set to: vp8\n&#x27; &#x2B;&#xA;  &#x27;[sdp @ 0x55604dc58400] audio codec set to: opus\n&#x27; &#x2B;&#xA;  &#x27;[sdp @ 0x55604dc58400] audio samplerate set to: 48000\n&#x27; &#x2B;&#xA;  &#x27;[sdp @ 0x55604dc58400] audio channels set to: 2\n&#x27; &#x2B;&#xA;  &#x27;[udp @ 0x55604dc6c500] end receive buffer size reported is 425984\n&#x27; &#x2B;&#xA;  &#x27;[udp @ 0x55604dc6c7c0] end receive buffer size reported is 425984\n&#x27; &#x2B;&#xA;  &#x27;[sdp @ 0x55604dc58400] setting jitter buffer size to 500\n&#x27; &#x2B;&#xA;  &#x27;[udp @ 0x55604dc6d900] end receive buffer size reported is 425984\n&#x27; &#x2B;&#xA;  &#x27;[udp @ 0x55604dc6d2c0] end receive buffer size reported is 425984\n&#x27; &#x2B;&#xA;  &#x27;[sdp @ 0x55604dc58400] setting jitter buffer size to 500\n&#x27;]&#xA;ffmpeg::process::data [data:&#x27;[sdp @ 0x55604dc58400] Before avformat_find_stream_info() pos: 210 bytes read:210 seeks:0 nb_streams:2\n&#x27;]&#xA;  **mediasoup:Consumer resume() &#x2B;1s**&#xA;  **mediasoup:Channel request() [method:consumer.resume, id:12] &#x2B;1s**&#xA;  **mediasoup:Channel request succeeded [method:consumer.resume, id:12] &#x2B;0ms**&#xA;  **mediasoup:Consumer resume() &#x2B;1ms**&#xA;  **mediasoup:Channel request() [method:consumer.resume, id:13] &#x2B;0ms**&#xA;  **mediasoup:Channel request succeeded [method:consumer.resume, id:13] &#x2B;0ms**&#xA;ffmpeg::process::data [data:&#x27;[sdp @ 0x55604dc58400] Could not find codec parameters for stream 0 (Video: vp8, 1 reference frame, yuv420p): unspecified size\n&#x27; &#x2B;&#xA;  "Consider increasing the value for the &#x27;analyzeduration&#x27; (0) and &#x27;probesize&#x27; (5000000) options\n"]&#xA;ffmpeg::process::data [data:&#x27;[sdp @ 0x55604dc58400] After avformat_find_stream_info() pos: 210 bytes read:210 seeks:0 frames:0\n&#x27; &#x2B;&#xA;  "Input #0, sdp, from &#x27;pipe:0&#x27;:\n" &#x2B;&#xA;  &#x27;  Metadata:\n&#x27; &#x2B;&#xA;  &#x27;    title           : FFmpeg\n&#x27; &#x2B;&#xA;  &#x27;  Duration: N/A, bitrate: N/A\n&#x27; &#x2B;&#xA;  &#x27;  Stream #0:0, 0, 1/90000: Video: vp8, 1 reference frame, yuv420p, 90k tbr, 90k tbn, 90k tbc\n&#x27; &#x2B;&#xA;  &#x27;  Stream #0:1, 0, 1/48000: Audio: opus, 48000 Hz, stereo, fltp\n&#x27; &#x2B;&#xA;  &#x27;Successfully opened the file.\n&#x27; &#x2B;&#xA;  &#x27;Parsing a group of options: output url storage/recordings/26e63cb3-4f81-499e-941a-c0bb7f7f52ce.webm.\n&#x27; &#x2B;&#xA;  &#x27;Applying option map (set input stream mapping) with argument 0:v:0.\n&#x27; &#x2B;&#xA;  &#x27;Applying option c:v (codec name) with argument copy.\n&#x27; &#x2B;&#xA;  &#x27;Applying option map (set input stream mapping) with argument 0:a:0.\n&#x27; &#x2B;&#xA;  &#x27;Applying option c:a (codec name) with argument copy.\n&#x27; &#x2B;&#xA;  &#x27;Applying option f (force format) with argument webm.\n&#x27; &#x2B;&#xA;  &#x27;Successfully parsed a group of options.\n&#x27; &#x2B;&#xA;  &#x27;Opening an output file: storage/recordings/26e63cb3-4f81-499e-941a-c0bb7f7f52ce.webm.\n&#x27; &#x2B;&#xA;  "[file @ 0x55604dce5bc0] Setting default whitelist &#x27;file,crypto,data&#x27;\n"]&#xA;ffmpeg::process::data [data:&#x27;Successfully opened the file.\n&#x27; &#x2B;&#xA;  &#x27;[webm @ 0x55604dce0fc0] dimensions not set\n&#x27; &#x2B;&#xA;  &#x27;Could not write header for output file #0 (incorrect codec parameters ?): Invalid argument\n&#x27; &#x2B;&#xA;  &#x27;Error initializing output stream 0:1 -- \n&#x27; &#x2B;&#xA;  &#x27;Stream mapping:\n&#x27; &#x2B;&#xA;  &#x27;  Stream #0:0 -> #0:0 (copy)\n&#x27; &#x2B;&#xA;  &#x27;  Stream #0:1 -> #0:1 (copy)\n&#x27; &#x2B;&#xA;  &#x27;    Last message repeated 1 times\n&#x27; &#x2B;&#xA;  &#x27;[AVIOContext @ 0x55604dc6dcc0] Statistics: 0 seeks, 0 writeouts\n&#x27; &#x2B;&#xA;  &#x27;[AVIOContext @ 0x55604dc69380] Statistics: 210 bytes read, 0 seeks\n&#x27;]&#xA;ffmpeg::process::close&#xA;&#xA;

    &#xA;

    FFmpeg says dimensions not  set and Could not write header for output file when I use Firefox. This might be enough for understanding the problem, but if you need more information you can read how server side is performing.&#xA;Server-Side in summary can be something like this :&#xA;lets say we initialized worker and router at run time using following functions.

    &#xA;

        // Start the mediasoup workers&#xA;module.exports.initializeWorkers = async () => {&#xA;  const { logLevel, logTags, rtcMinPort, rtcMaxPort } = config.worker;&#xA;&#xA;  console.log(&#x27;initializeWorkers() creating %d mediasoup workers&#x27;, config.numWorkers);&#xA;&#xA;  for (let i = 0; i &lt; config.numWorkers; &#x2B;&#x2B;i) {&#xA;    const worker = await mediasoup.createWorker({&#xA;      logLevel, logTags, rtcMinPort, rtcMaxPort&#xA;    });&#xA;&#xA;    worker.once(&#x27;died&#x27;, () => {&#xA;      console.error(&#x27;worker::died worker has died exiting in 2 seconds... [pid:%d]&#x27;, worker.pid);&#xA;      setTimeout(() => process.exit(1), 2000);&#xA;    });&#xA;&#xA;    workers.push(worker);&#xA;  }&#xA;};&#xA;

    &#xA;

    module.exports.createRouter = async () => {&#xA;  const worker = getNextWorker();&#xA;&#xA;  console.log(&#x27;createRouter() creating new router [worker.pid:%d]&#x27;, worker.pid);&#xA;&#xA;  console.log(`config.router.mediaCodecs:${JSON.stringify(config.router.mediaCodecs)}`)&#xA;&#xA;  return await worker.createRouter({ mediaCodecs: config.router.mediaCodecs });&#xA;};&#xA;

    &#xA;

    We pass router.rtpCompatibilities to the client. clients get the rtpCompatibilities and create a device and loads it. after that a transport must be created at server side.

    &#xA;

        const handleCreateTransportRequest = async (jsonMessage) => {&#xA;&#xA;  const transport = await createTransport(&#x27;webRtc&#x27;, router);&#xA;&#xA;  var peer;&#xA;  try {peer = peers.get(jsonMessage.sessionId);}&#xA;  catch{console.log(&#x27;peer not found&#x27;)}&#xA;  &#xA;  peer.addTransport(transport);&#xA;&#xA;  peer.socket.emit(&#x27;create-transport&#x27;,{&#xA;    id: transport.id,&#xA;    iceParameters: transport.iceParameters,&#xA;    iceCandidates: transport.iceCandidates,&#xA;    dtlsParameters: transport.dtlsParameters&#xA;  });&#xA;};&#xA;

    &#xA;

    Then after the client side also created the transport we listen to connect event an at the time of event, we request the server to create connection.

    &#xA;

    const handleTransportConnectRequest = async (jsonMessage) => {&#xA;  var peer;&#xA;  try {peer = peers.get(jsonMessage.sessionId);}&#xA;  catch{console.log(&#x27;peer not found&#x27;)}&#xA;&#xA;  if (!peer) {&#xA;    throw new Error(`Peer with id ${jsonMessage.sessionId} was not found`);&#xA;  }&#xA;&#xA;  const transport = peer.getTransport(jsonMessage.transportId);&#xA;&#xA;  if (!transport) {&#xA;    throw new Error(`Transport with id ${jsonMessage.transportId} was not found`);&#xA;  }&#xA;&#xA;  await transport.connect({ dtlsParameters: jsonMessage.dtlsParameters });&#xA;  console.log(&#x27;handleTransportConnectRequest() transport connected&#x27;);&#xA;  peer.socket.emit(&#x27;connect-transport&#x27;);&#xA;};&#xA;

    &#xA;

    Similar thing happen on produce event.

    &#xA;

    const handleProduceRequest = async (jsonMessage) => {&#xA;  console.log(&#x27;handleProduceRequest [data:%o]&#x27;, jsonMessage);&#xA;&#xA;  var peer;&#xA;  try {peer = peers.get(jsonMessage.sessionId);}&#xA;  catch{console.log(&#x27;peer not found&#x27;)}&#xA;&#xA;  if (!peer) {&#xA;    throw new Error(`Peer with id ${jsonMessage.sessionId} was not found`);&#xA;  }&#xA;&#xA;  const transport = peer.getTransport(jsonMessage.transportId);&#xA;&#xA;  if (!transport) {&#xA;    throw new Error(`Transport with id ${jsonMessage.transportId} was not found`);&#xA;  }&#xA;&#xA;  const producer = await transport.produce({&#xA;    kind: jsonMessage.kind,&#xA;    rtpParameters: jsonMessage.rtpParameters&#xA;  });&#xA;&#xA;  peer.addProducer(producer);&#xA;&#xA;  console.log(&#x27;handleProducerRequest() new producer added [id:%s, kind:%s]&#x27;, producer.id, producer.kind);&#xA;&#xA;  peer.socket.emit(&#x27;produce&#x27;,{&#xA;    id: producer.id,&#xA;    kind: producer.kind&#xA;  });&#xA;};&#xA;

    &#xA;

    For Recording, first I create plain transports for audio and video producers.

    &#xA;

    const rtpTransport = router.createPlainTransport(config.plainRtpTransport);&#xA;

    &#xA;

    then rtp transport must be connected to ports :

    &#xA;

      await rtpTransport.connect({&#xA;    ip: &#x27;127.0.0.1&#x27;,&#xA;    port: remoteRtpPort,&#xA;    rtcpPort: remoteRtcpPort&#xA;  });&#xA;

    &#xA;

    Then the consumer must also be created.

    &#xA;

      const rtpConsumer = await rtpTransport.consume({&#xA;    producerId: producer.id,&#xA;    rtpCapabilities,&#xA;    paused: true&#xA;  });&#xA;

    &#xA;

    After that we can start recording using following code :

    &#xA;

     this._rtpParameters = args;&#xA;    this._process = undefined;&#xA;    this._observer = new EventEmitter();&#xA;    this._peer = args.peer;&#xA;&#xA;    this._sdpString = createSdpText(this._rtpParameters);&#xA;    this._sdpStream = convertStringToStream(this._sdpString);&#xA;    // create dir&#xA;    const dir = process.env.REOCRDING_PATH ?? &#x27;storage/recordings&#x27;;&#xA;    if (!fs.existsSync(dir)) shelljs.mkdir(&#x27;-p&#x27;, dir);&#xA;  &#xA;    this._extension = &#x27;webm&#x27;;&#xA;    // create file path&#xA;    this._path = `${dir}/${args.peer.sessionId}.${this._extension}`&#xA;    let loop = 0;&#xA;    while(fs.existsSync(this._path)) {&#xA;      this._path = `${dir}/${args.peer.sessionId}-${&#x2B;&#x2B;loop}.${this._extension}`&#xA;    }&#xA;&#xA;this._recordingnModel = await Recording.findOne({sessionIds: { $in: [this._peer.sessionId] }})&#xA;    this._recordingnModel.files.push(this._path);&#xA;    this._recordingnModel.save();&#xA;&#xA;let proc  = ffmpeg(this._sdpStream)&#xA;    .inputOptions([&#xA;      &#x27;-protocol_whitelist&#x27;,&#x27;pipe,udp,rtp&#x27;,&#xA;      &#x27;-f&#x27;,&#x27;sdp&#x27;,&#xA;    ])&#xA;    .format(this._extension)&#xA;    .output(this._path)&#xA;    .size(&#x27;720x?&#x27;)&#xA;    .on(&#x27;start&#x27;, ()=>{&#xA;      this._peer.socket.emit(&#x27;recording&#x27;);&#xA;    })&#xA;    .on(&#x27;end&#x27;, ()=>{&#xA;      let path = this._path.replace(&#x27;storage/recordings/&#x27;, &#x27;&#x27;);&#xA;      this._peer.socket.emit(&#x27;recording-closed&#x27;, {&#xA;        url: `${process.env.APP_URL}/recording/file/${path}`&#xA;      });&#xA;    });&#xA;&#xA;    proc.run();&#xA;    this._process =  proc;&#xA;  }&#xA;&#xA;

    &#xA;

  • iOs : Low frame per second(fps) for VGA resolution

    26 juillet 2014, par Bhuvan Balasubramanian

    I’m facing an issue in broadcasting video from one iPhone to another iPhone.

    The issue is when I view the friend’s live video in my iPhone, the frame per second(fps) is very low(it is 12fps). Video quality and audio is looking fine but the only problem is fps.

    I don’t know where I need to config/change the code to convert from variable fps to constant fps. Also to increase the fps as **24/30**.

    The resolution I used for broadcasting

    RESOLUTION_VGA,     // 480x640px (landscape) &amp; 640x480px (portrait)

    I’m using following libraries for streaming

    1. MediaLibiOS - link
    2. Ffmpeg-2.2.1
    3. CommLibiOS
    4. libx264-r2409

    Wowza is a Media Server and iOS target version is 7.0

    Please help !

    Thanks in advance.