Recherche avancée

Médias (1)

Mot : - Tags -/illustrator

Autres articles (75)

  • Personnaliser en ajoutant son logo, sa bannière ou son image de fond

    5 septembre 2013, par

    Certains thèmes prennent en compte trois éléments de personnalisation : l’ajout d’un logo ; l’ajout d’une bannière l’ajout d’une image de fond ;

  • Ecrire une actualité

    21 juin 2013, par

    Présentez les changements dans votre MédiaSPIP ou les actualités de vos projets sur votre MédiaSPIP grâce à la rubrique actualités.
    Dans le thème par défaut spipeo de MédiaSPIP, les actualités sont affichées en bas de la page principale sous les éditoriaux.
    Vous pouvez personnaliser le formulaire de création d’une actualité.
    Formulaire de création d’une actualité Dans le cas d’un document de type actualité, les champs proposés par défaut sont : Date de publication ( personnaliser la date de publication ) (...)

  • Les vidéos

    21 avril 2011, par

    Comme les documents de type "audio", Mediaspip affiche dans la mesure du possible les vidéos grâce à la balise html5 .
    Un des inconvénients de cette balise est qu’elle n’est pas reconnue correctement par certains navigateurs (Internet Explorer pour ne pas le nommer) et que chaque navigateur ne gère en natif que certains formats de vidéos.
    Son avantage principal quant à lui est de bénéficier de la prise en charge native de vidéos dans les navigateur et donc de se passer de l’utilisation de Flash et (...)

Sur d’autres sites (10061)

  • Matomo analytics for wordpress

    15 octobre 2019, par Matomo Core Team — Community

    Self-hosting web analytics got a whole lot easier ! Introducing Matomo for WordPress

    Be the first to try it out ! Your feedback is much needed and appreciated

    Get a fully functioning Matomo (which is comparable to Google Analytics) in seconds ! How ? With the new Matomo Analytics for WordPress plugin. 

    Web analytics in WordPress has never been easier to get, or more powerful. Matomo Analytics for WordPress is the one-stop problem solver. It’ll save you time, money and give you the insights to help your website or business succeed. 

    Best of all, we get to further the goal of decentralising the internet. Our hope is for Matomo Analytics for WordPress to spread far and wide. We’re so excited that more and more people can now get their hands on this powerful, free, open-source analytics platform, in a few clicks !

    Download now and check it out !

    What do you get ?

    • No more signing up to third party analytics service (like Google)
    • No more sending away your valuable data to a third party service (like Google)
    • Easy setup – install with a few clicks, no tracking code installation or developer knowledge needed
    • 100% accurate data – no data sampling and no data limits 
    • Full data ownership – all data is stored on your servers and no one else can see your data
    • Privacy protection / GDPR compliance
    • Ecommerce tracking out-of-the-box (Woocommerce, Easy Digital Downloads, and MemberPress) and we’re keen to add many more over time
    • Powerful features – segmenting, comparing reports, different visualisations, real-time reports, visit logs and visitor profiles, Matomo Tag Manager, dashboards, data export, APIs, and many more
    • Compared to other WordPress solutions we don’t charge you extra for basic features that should work out-of-the-box
    • Just like Matomo On-Premise, Matomo Analytics for WordPress is free

    We need your feedback !

    We all know and love the versatility of WordPress – with over 55,000 plugins and all the different ways of hosting it. However, with this great versatility comes the potential for things to be missed, so we’re keen to hear your feedback.

    Thank you ! We really appreciate your help on this ❤️

    How do you get Matomo Analytics for WordPress ?

    You can then upload it in your WordPress by going to “Plugins => Add New”. During the upload, if you get an error like “Are you sure you want to do this ?”, we recommend you upload the extracted zip file on to your server and into your ‘wp-content/plugins’ folder manually using ftp or ssh. Make sure the plugin name is ‘matomo’.

    All you need is at least WordPress 4.8 and PHP 7.0 or later. MySQL 5.1+ is recommended. 

    The source code is available at : https://github.com/matomo-org/wp-matomo/

    In perfect harmony : Matomo and WordPress

    Matomo Analytics for WordPress

    The idea for this started two years ago when we realised the similarities between the Matomo and WordPress project. 

    Not only from a technological point of view – where both are based on PHP and MySQL and can be extended using plugins – but also from a philosophical, license and values point of view. We both believe in privacy, security, data ownership, openness, transparency, having things working out-of-the-box, simplicity etc. 

    WordPress is currently used on approximately 30% of all websites. Many of them use the self-hosted open-source WordPress version. Giving everyone in this market the opportunity to easily get a powerful web analytics platform for free, means a lot to us. We believe WordPress users get a real choice besides the standard solution of Google Analytics, and it furthers our effort and goal of decentralising the internet. 

    We’re hoping more people will be empowered to protect user privacy, have access to a great free and open-source tool, and keep control of data in their own hands.

    We hope you feel the same. Help us spread the word to your friends and get them in on this awesome new project !

    Share on facebook
    Share on twitter
    Share on linkedin

    FAQs

    Isn’t there already a WP-Matomo plugin for WordPress available ?

    Yes, the existing WP-Matomo (WP-Piwik) plugin is an awesome plugin to connect your existing Matomo On-Premise or Matomo Cloud account with WordPress. The difference is that this new plugin installs Matomo Analytics fully in your WordPress. So you get the convenience of having a powerful analytics platform within your WordPress.

    We highly recommend you install this new plugin if you use WordPress and are not running Matomo yet. 

    If you are already using Matomo on our Cloud or On-Premise, we’d still highly recommend you use WP-Matomo (WP-Piwik). So that you get an easier way of inserting the tracking code into your WordPress site and get insights faster.

    I have a high traffic website, will it be an issue ?

    If you have a lot of traffic, we’d advise you to install Matomo On-Premise separately. There’s no specific traffic threshold we can give you on when it’s better to use Matomo On-Premise. It really depends on your server. 

    We reckon if you have more than 500,000 page views a month, you may want to think about using Matomo On-Premise with WP-Matomo instead, but this is just an estimate. In general, if the load on your server is already quite high, then it might be better to install Matomo on a separate server. See also recommended server sizing for running Matomo.

    How do I report a bug or request a new feature in Matomo for WordPress ?

    Please create an issue, on our repository whenever you find a bug or if you have any suggestion or ideas of improvement. We want to build an outstanding analytics experience for WordPress !

    Have another question you’re dying to ask ? The Matomo for WordPress FAQ page might have the answer you need. 

    Matomo Analytics for WordPress newsletter

    Get ahead of the crowd – signup to our exclusive Matomo for WordPress newsletter to get the latest updates on this exciting new project.

    &lt;script type=&quot;text/javascript&quot;&gt;<br />
    (function(global) {<br />
     function serialize(form){if(!form||form.nodeName!==&quot;FORM&quot;){return }var i,j,q=[];for(i=form.elements.length-1;i&gt;=0;i=i-1){if(form.elements[i].name===&quot;&quot;){continue}switch(form.elements[i].nodeName){case&quot;INPUT&quot;:switch(form.elements[i].type){case&quot;text&quot;:case&quot;hidden&quot;:case&quot;password&quot;:case&quot;button&quot;:case&quot;reset&quot;:case&quot;submit&quot;:q.push(form.elements[i].name+&quot;=&quot;+encodeURIComponent(form.elements[i].value));break;case&quot;checkbox&quot;:case&quot;radio&quot;:if(form.elements[i].checked){q.push(form.elements[i].name+&quot;=&quot;+encodeURIComponent(form.elements[i].value))}break;case&quot;file&quot;:break}break;case&quot;TEXTAREA&quot;:q.push(form.elements[i].name+&quot;=&quot;+encodeURIComponent(form.elements[i].value));break;case&quot;SELECT&quot;:switch(form.elements[i].type){case&quot;select-one&quot;:q.push(form.elements[i].name+&quot;=&quot;+encodeURIComponent(form.elements[i].value));break;case&quot;select-multiple&quot;:for(j=form.elements[i].options.length-1;j&gt;=0;j=j-1){if(form.elements[i].options[j].selected){q.push(form.elements[i].name+&quot;=&quot;+encodeURIComponent(form.elements[i].options[j].value))}}break}break;case&quot;BUTTON&quot;:switch(form.elements[i].type){case&quot;reset&quot;:case&quot;submit&quot;:case&quot;button&quot;:q.push(form.elements[i].name+&quot;=&quot;+encodeURIComponent(form.elements[i].value));break}break}}return q.join(&quot;&amp;&quot;)};<br />
    <br />
    <br />
     function extend(destination, source) {<br />
       for (var prop in source) {<br />
         destination[prop] = source[prop];<br />
       }<br />
     }<br />
    <br />
     if (!Mimi) var Mimi = {};<br />
     if (!Mimi.Signups) Mimi.Signups = {};<br />
    <br />
     Mimi.Signups.EmbedValidation = function() {<br />
       this.initialize();<br />
    <br />
       var _this = this;<br />
       if (document.addEventListener) {<br />
         this.form.addEventListener('submit', function(e){<br />
           _this.onFormSubmit(e);<br />
         });<br />
       } else {<br />
         this.form.attachEvent('onsubmit', function(e){<br />
           _this.onFormSubmit(e);<br />
         });<br />
       }<br />
     };<br />
    <br />
     extend(Mimi.Signups.EmbedValidation.prototype, {<br />
       initialize: function() {<br />
         this.form         = document.getElementById('ema_signup_form');<br />
         this.submit       = document.getElementById('webform_submit_button');<br />
         this.callbackName = 'jsonp_callback_' + Math.round(100000 * Math.random());<br />
         this.validEmail   = /.+@.+\..+/<br />
       },<br />
    <br />
       onFormSubmit: function(e) {<br />
         e.preventDefault();<br />
    <br />
         this.validate();<br />
         if (this.isValid) {<br />
           this.submitForm();<br />
         } else {<br />
           this.revalidateOnChange();<br />
         }<br />
       },<br />
    <br />
       validate: function() {<br />
         this.isValid = true;<br />
         this.emailValidation();<br />
         this.fieldAndListValidation();<br />
         this.updateFormAfterValidation();<br />
       },<br />
    <br />
       emailValidation: function() {<br />
         var email = document.getElementById('signup_email');<br />
    <br />
         if (this.validEmail.test(email.value)) {<br />
           this.removeTextFieldError(email);<br />
         } else {<br />
           this.textFieldError(email);<br />
           this.isValid = false;<br />
         }<br />
       },<br />
    <br />
       fieldAndListValidation: function() {<br />
         var fields = this.form.querySelectorAll('.mimi_field.required');<br />
    <br />
         for (var i = 0; i &lt; fields.length; ++i) {<br />
           var field = fields[i],<br />
               type  = this.fieldType(field);<br />
           if (type === 'checkboxes' || type === 'radio_buttons' || type === 'age_check') {<br />
             this.checkboxAndRadioValidation(field);<br />
           } else {<br />
             this.textAndDropdownValidation(field, type);<br />
           }<br />
         }<br />
       },<br />
    <br />
       fieldType: function(field) {<br />
         var type = field.querySelectorAll('.field_type');<br />
    <br />
         if (type.length) {<br />
           return type[0].getAttribute('data-field-type');<br />
         } else if (field.className.indexOf('checkgroup') &gt;= 0) {<br />
           return 'checkboxes';<br />
         } else {<br />
           return 'text_field';<br />
         }<br />
       },<br />
    <br />
       checkboxAndRadioValidation: function(field) {<br />
         var inputs   = field.getElementsByTagName('input'),<br />
             selected = false;<br />
    <br />
         for (var i = 0; i &lt; inputs.length; ++i) {<br />
           var input = inputs[i];<br />
           if((input.type === 'checkbox' || input.type === 'radio') &amp;&amp; input.checked) {<br />
             selected = true;<br />
           }<br />
         }<br />
    <br />
         if (selected) {<br />
           field.className = field.className.replace(/ invalid/g, '');<br />
         } else {<br />
           if (field.className.indexOf('invalid') === -1) {<br />
             field.className += ' invalid';<br />
           }<br />
    <br />
           this.isValid = false;<br />
         }<br />
       },<br />
    <br />
       textAndDropdownValidation: function(field, type) {<br />
         var inputs = field.getElementsByTagName('input');<br />
    <br />
         for (var i = 0; i &lt; inputs.length; ++i) {<br />
           var input = inputs[i];<br />
           if (input.name.indexOf('signup') &gt;= 0) {<br />
             if (type === 'text_field') {<br />
               this.textValidation(input);<br />
             } else {<br />
               this.dropdownValidation(field, input);<br />
             }<br />
           }<br />
         }<br />
         this.htmlEmbedDropdownValidation(field);<br />
       },<br />
    <br />
       textValidation: function(input) {<br />
         if (input.id === 'signup_email') return;<br />
    <br />
         if (input.value) {<br />
           this.removeTextFieldError(input);<br />
         } else {<br />
           this.textFieldError(input);<br />
           this.isValid = false;<br />
         }<br />
       },<br />
    <br />
       dropdownValidation: function(field, input) {<br />
         if (input.value) {<br />
           field.className = field.className.replace(/ invalid/g, '');<br />
         } else {<br />
           if (field.className.indexOf('invalid') === -1) field.className += ' invalid';<br />
           this.onSelectCallback(input);<br />
           this.isValid = false;<br />
         }<br />
       },<br />
    <br />
       htmlEmbedDropdownValidation: function(field) {<br />
         var dropdowns = field.querySelectorAll('.mimi_html_dropdown');<br />
         var _this = this;<br />
    <br />
         for (var i = 0; i &lt; dropdowns.length; ++i) {<br />
           var dropdown = dropdowns[i];<br />
    <br />
           if (dropdown.value) {<br />
             field.className = field.className.replace(/ invalid/g, '');<br />
           } else {<br />
             if (field.className.indexOf('invalid') === -1) field.className += ' invalid';<br />
             this.isValid = false;<br />
             dropdown.onchange = (function(){ _this.validate(); });<br />
           }<br />
         }<br />
       },<br />
    <br />
       textFieldError: function(input) {<br />
         input.className   = 'required invalid';<br />
         input.placeholder = input.getAttribute('data-required-field');<br />
       },<br />
    <br />
       removeTextFieldError: function(input) {<br />
         input.className   = 'required';<br />
         input.placeholder = '';<br />
       },<br />
    <br />
       onSelectCallback: function(input) {<br />
         if (typeof Widget === 'undefined' || !Widget.BasicDropdown) return;<br />
    <br />
         var dropdownEl = input.parentNode,<br />
             instances  = Widget.BasicDropdown.instances,<br />
             _this = this;<br />
    <br />
         for (var i = 0; i &lt; instances.length; ++i) {<br />
           var instance = instances[i];<br />
           if (instance.wrapperEl === dropdownEl) {<br />
             instance.onSelect = function(){ _this.validate() };<br />
           }<br />
         }<br />
       },<br />
    <br />
       updateFormAfterValidation: function() {<br />
         this.form.className   = this.setFormClassName();<br />
         this.submit.value     = this.submitButtonText();<br />
         this.submit.disabled  = !this.isValid;<br />
         this.submit.className = this.isValid ? 'submit' : 'disabled';<br />
       },<br />
    <br />
       setFormClassName: function() {<br />
         var name = this.form.className;<br />
    <br />
         if (this.isValid) {<br />
           return name.replace(/\s?mimi_invalid/, '');<br />
         } else {<br />
           if (name.indexOf('mimi_invalid') === -1) {<br />
             return name += ' mimi_invalid';<br />
           } else {<br />
             return name;<br />
           }<br />
         }<br />
       },<br />
    <br />
       submitButtonText: function() {<br />
         var invalidFields = document.querySelectorAll('.invalid'),<br />
             text;<br />
    <br />
         if (this.isValid || !invalidFields) {<br />
           text = this.submit.getAttribute('data-default-text');<br />
         } else {<br />
           if (invalidFields.length || invalidFields[0].className.indexOf('checkgroup') === -1) {<br />
             text = this.submit.getAttribute('data-invalid-text');<br />
           } else {<br />
             text = this.submit.getAttribute('data-choose-list');<br />
           }<br />
         }<br />
         return text;<br />
       },<br />
    <br />
       submitForm: function() {<br />
         this.formSubmitting();<br />
    <br />
         var _this = this;<br />
         window[this.callbackName] = function(response) {<br />
           delete window[this.callbackName];<br />
           document.body.removeChild(script);<br />
           _this.onSubmitCallback(response);<br />
         };<br />
    <br />
         var script = document.createElement('script');<br />
         script.src = this.formUrl('json');<br />
         document.body.appendChild(script);<br />
       },<br />
    <br />
       formUrl: function(format) {<br />
         var action  = this.form.action;<br />
         if (format === 'json') action += '.json';<br />
         return action + '?callback=' + this.callbackName + '&amp;' + serialize(this.form);<br />
       },<br />
    <br />
       formSubmitting: function() {<br />
         this.form.className  += ' mimi_submitting';<br />
         this.submit.value     = this.submit.getAttribute('data-submitting-text');<br />
         this.submit.disabled  = true;<br />
         this.submit.className = 'disabled';<br />
       },<br />
    <br />
       onSubmitCallback: function(response) {<br />
         if (response.success) {<br />
           this.onSubmitSuccess(response.result);<br />
         } else {<br />
           top.location.href = this.formUrl('html');<br />
         }<br />
       },<br />
    <br />
       onSubmitSuccess: function(result) {<br />
         if (result.has_redirect) {<br />
           top.location.href = result.redirect;<br />
         } else if(result.single_opt_in || !result.confirmation_html) {<br />
           this.disableForm();<br />
           this.updateSubmitButtonText(this.submit.getAttribute('data-thanks'));<br />
         } else {<br />
           this.showConfirmationText(result.confirmation_html);<br />
         }<br />
       },<br />
    <br />
       showConfirmationText: function(html) {<br />
         var fields = this.form.querySelectorAll('.mimi_field');<br />
    <br />
         for (var i = 0; i &lt; fields.length; ++i) {<br />
           fields[i].style['display'] = 'none';<br />
         }<br />
    <br />
         (this.form.querySelectorAll('fieldset')[0] || this.form).innerHTML = html;<br />
       },<br />
    <br />
       disableForm: function() {<br />
         var elements = this.form.elements;<br />
         for (var i = 0; i &lt; elements.length; ++i) {<br />
           elements[i].disabled = true;<br />
         }<br />
       },<br />
    <br />
       updateSubmitButtonText: function(text) {<br />
         this.submit.value = text;<br />
       },<br />
    <br />
       revalidateOnChange: function() {<br />
         var fields = this.form.querySelectorAll(&quot;.mimi_field.required&quot;),<br />
             _this = this;<br />
    <br />
         var onTextFieldChange = function() {<br />
           if (this.getAttribute('name') === 'signup[email]') {<br />
             if (_this.validEmail.test(this.value)) _this.validate();<br />
           } else {<br />
             if (this.value.length === 1) _this.validate();<br />
           }<br />
         }<br />
    <br />
         for (var i = 0; i &lt; fields.length; ++i) {<br />
           var inputs = fields[i].getElementsByTagName('input');<br />
           for (var j = 0; j &lt; inputs.length; ++j) {<br />
             if (this.fieldType(fields[i]) === 'text_field') {<br />
               inputs[j].onkeyup = onTextFieldChange;<br />
               inputs[j].onchange = onTextFieldChange; <br />
             } else {<br />
               inputs[j].onchange = function(){ _this.validate() };<br />
             }<br />
           }<br />
         }<br />
       }<br />
     });<br />
    <br />
     if (document.addEventListener) {<br />
       document.addEventListener(&quot;DOMContentLoaded&quot;, function() {<br />
         new Mimi.Signups.EmbedValidation();<br />
       });<br />
     }<br />
     else {<br />
       window.attachEvent('onload', function() {<br />
         new Mimi.Signups.EmbedValidation();<br />
       });<br />
     }<br />
    })(this);<br />
    &lt;/script&gt;
  • ffmpeg concat doesn't keep video speed/framerate ?

    28 août 2016, par Nick

    I have a bunch of small webm clips in directory vidchunks. These clips were generated using javascript MediaRecorder API.

    MediaRecorder code :

    var mRecorder;
    var chunks = [];
    navigator.mediaDevices.getUserMedia({
     audio: false,
     video: {
     width: 1280,
     height: 760,
     mozMediaSource: "screen",
     mediaSource: "screen"
    }
    }).then(function(stream) {
     mRecorder = new MediaRecorder(stream,{mimeType:"video/webm"});
     mRecorder.ondataavailable = function(event) {
       var blob = event.data;
       chunks.push(blob);
       var vidchunk = new Blob(chunks);
       // save to directory vid chunk.
     };
     mRecorder.start(5000);
    }).catch(function(error) {
     console.log(error.message);
    });

    Then, once I have a bunch of these 5 second clips, I merge them :

    ffmpeg -safe 0 \
    -f concat -i &lt;(for f in `ls vidchunks/* | sort -V`; do echo file $f; done) \
    -c:v copy -r 30 -vsync drop test.webm

    When I play back these chunks individually, they play correctly. All 5 seconds of video are rendered smoothly.

    However, once I merge, some 5 second chunks are condensed to like 1 second. So the total video, which should be 50 seconds for 10 chunks, turns out to be like 38 seconds because three of the chunks got condensed to 1 second.

    Any ideas on how to fix this ?

    EDIT : I’ve tried :

    • without "-r 30 -vsync drop"
    • without "-c:v copy"

    No changes...

    EDIT2 (some ffprobe outputs) :

    ffprobe 1472343170-1.webm

    ffprobe version 2.8.6-1ubuntu2 Copyright (c) 2007-2016 the FFmpeg developers
     built with gcc 5.3.1 (Ubuntu 5.3.1-11ubuntu1) 20160311
     configuration: --prefix=/usr --extra-version=1ubuntu2 --build-suffix=-ffmpeg --toolchain=hardened --libdir=/usr/lib/x86_64-linux-gnu --incdir=/usr/include/x86_64-linux-gnu --cc=cc --cxx=g++ --enable-gpl --enable-shared --disable-stripping --disable-decoder=libopenjpeg --disable-decoder=libschroedinger --enable-avresample --enable-avisynth --enable-gnutls --enable-ladspa --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme --enable-libgsm --enable-libmodplug --enable-libmp3lame --enable-libopenjpeg --enable-libopus --enable-libpulse --enable-librtmp --enable-libschroedinger --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libssh --enable-libtheora --enable-libtwolame --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx265 --enable-libxvid --enable-libzvbi --enable-openal --enable-opengl --enable-x11grab --enable-libdc1394 --enable-libiec61883 --enable-libzmq --enable-frei0r --enable-libx264 --enable-libopencv
     libavutil      54. 31.100 / 54. 31.100
     libavcodec     56. 60.100 / 56. 60.100
     libavformat    56. 40.101 / 56. 40.101
     libavdevice    56.  4.100 / 56.  4.100
     libavfilter     5. 40.101 /  5. 40.101
     libavresample   2.  1.  0 /  2.  1.  0
     libswscale      3.  1.101 /  3.  1.101
     libswresample   1.  2.101 /  1.  2.101
     libpostproc    53.  3.100 / 53.  3.100
    Input #0, matroska,webm, from '1472343170-1.webm':
     Metadata:
       encoder         : Chrome
     Duration: N/A, start: 0.000000, bitrate: N/A
       Stream #0:0(eng): Video: vp8, yuv420p, 1280x760, SAR 1:1 DAR 32:19, 30 fps, 30 tbr, 1k tbn, 1k tbc (default)

    ffprobe 1472343245-16.webm

    ffprobe version 2.8.6-1ubuntu2 Copyright (c) 2007-2016 the FFmpeg developers
     built with gcc 5.3.1 (Ubuntu 5.3.1-11ubuntu1) 20160311
     configuration: --prefix=/usr --extra-version=1ubuntu2 --build-suffix=-ffmpeg --toolchain=hardened --libdir=/usr/lib/x86_64-linux-gnu --incdir=/usr/include/x86_64-linux-gnu --cc=cc --cxx=g++ --enable-gpl --enable-shared --disable-stripping --disable-decoder=libopenjpeg --disable-decoder=libschroedinger --enable-avresample --enable-avisynth --enable-gnutls --enable-ladspa --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme --enable-libgsm --enable-libmodplug --enable-libmp3lame --enable-libopenjpeg --enable-libopus --enable-libpulse --enable-librtmp --enable-libschroedinger --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libssh --enable-libtheora --enable-libtwolame --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx265 --enable-libxvid --enable-libzvbi --enable-openal --enable-opengl --enable-x11grab --enable-libdc1394 --enable-libiec61883 --enable-libzmq --enable-frei0r --enable-libx264 --enable-libopencv
     libavutil      54. 31.100 / 54. 31.100
     libavcodec     56. 60.100 / 56. 60.100
     libavformat    56. 40.101 / 56. 40.101
     libavdevice    56.  4.100 / 56.  4.100
     libavfilter     5. 40.101 /  5. 40.101
     libavresample   2.  1.  0 /  2.  1.  0
     libswscale      3.  1.101 /  3.  1.101
     libswresample   1.  2.101 /  1.  2.101
     libpostproc    53.  3.100 / 53.  3.100
    Input #0, matroska,webm, from '1472343245-16.webm':
     Metadata:
       encoder         : Chrome
     Duration: N/A, start: 0.000000, bitrate: N/A
       Stream #0:0(eng): Video: vp8, yuv420p, 1280x760, SAR 1:1 DAR 32:19, 30 fps, 30 tbr, 1k tbn, 1k tbc (default)

    ffprobe 1472343350-37.webm

    ffprobe version 2.8.6-1ubuntu2 Copyright (c) 2007-2016 the FFmpeg developers
     built with gcc 5.3.1 (Ubuntu 5.3.1-11ubuntu1) 20160311
     configuration: --prefix=/usr --extra-version=1ubuntu2 --build-suffix=-ffmpeg --toolchain=hardened --libdir=/usr/lib/x86_64-linux-gnu --incdir=/usr/include/x86_64-linux-gnu --cc=cc --cxx=g++ --enable-gpl --enable-shared --disable-stripping --disable-decoder=libopenjpeg --disable-decoder=libschroedinger --enable-avresample --enable-avisynth --enable-gnutls --enable-ladspa --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme --enable-libgsm --enable-libmodplug --enable-libmp3lame --enable-libopenjpeg --enable-libopus --enable-libpulse --enable-librtmp --enable-libschroedinger --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libssh --enable-libtheora --enable-libtwolame --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx265 --enable-libxvid --enable-libzvbi --enable-openal --enable-opengl --enable-x11grab --enable-libdc1394 --enable-libiec61883 --enable-libzmq --enable-frei0r --enable-libx264 --enable-libopencv
     libavutil      54. 31.100 / 54. 31.100
     libavcodec     56. 60.100 / 56. 60.100
     libavformat    56. 40.101 / 56. 40.101
     libavdevice    56.  4.100 / 56.  4.100
     libavfilter     5. 40.101 /  5. 40.101
     libavresample   2.  1.  0 /  2.  1.  0
     libswscale      3.  1.101 /  3.  1.101
     libswresample   1.  2.101 /  1.  2.101
     libpostproc    53.  3.100 / 53.  3.100
    Input #0, matroska,webm, from '1472343350-37.webm':
     Metadata:
       encoder         : Chrome
     Duration: N/A, start: 0.000000, bitrate: N/A
       Stream #0:0(eng): Video: vp8, yuv420p, 1280x760, SAR 1:1 DAR 32:19, 30 fps, 30 tbr, 1k tbn, 1k tbc (default)

    I also noticed that the merged video plays normal when the mouse is moving around on the screen, but when the mouse is not moving, it moves 5x faster.

    I know this because I have a timer on the screen that I’m recording, and I can see that the timer speeds up significantly when mouse isn’t moving but when mouse is moving, the timer moves at normal speed.

    EDIT3 :
    The DTS/PTS looks like resets after each file is merged :

    DTS -9223363446915256, next:6866667 st:0 invalid dropping
    PTS -9223363446915256, next:6866667 invalid dropping st:0
    DTS -9223363446915222, next:6900000 st:0 invalid dropping
    PTS -9223363446915222, next:6900000 invalid dropping st:0
    DTS -9223363446915188, next:6933333 st:0 invalid dropping
    PTS -9223363446915188, next:6933333 invalid dropping st:0
    [concat @ 0x1cde3e0] DTS -9223363446920184 &lt; 5006 out of order
    DTS -9223363446920184, next:5033333 st:0 invalid dropping
    PTS -9223363446920184, next:5033333 invalid dropping st:0
    DTS -9223363446920056, next:5066667 st:0 invalid dropping
    PTS -9223363446920056, next:5066667 invalid dropping st:0
    DTS -9223363446919956, next:5100000 st:0 invalid dropping
    PTS -9223363446919956, next:5100000 invalid dropping st:0

    EDIT4 : Tried ffmpeg -i "$f" -y -c copy -fflags +genpts "$f" and then merge again.

    Works a lot better, but now other files are getting skipped.

    Here’s ffprobe from a file that’s skipped :

    Metadata:
       encoder         : Lavf56.40.101
     Duration: 00:00:01.17, start: 0.000000, bitrate: 428 kb/s
       Stream #0:0(eng): Video: vp8, yuv420p, 1280x760, SAR 1:1 DAR 32:19, 30 fps, 30 tbr, 1k tbn, 1k tbc (default)
    [webm @ 0x1c01940] Codec for stream 0 does not use global headers but container format requires global headers
    Output #0, webm, to '1472343225-12.webm':
     Metadata:
       encoder         : Lavf56.40.101
       Stream #0:0(eng): Video: vp8, yuv420p, 1280x760 [SAR 1:1 DAR 32:19], q=2-31, 30 fps, 30 tbr, 1k tbn, 1k tbc (default)
    Stream mapping:
     Stream #0:0 -> #0:0 (copy)

    Here’s ffprobe from a file not skipped :

    Input #0, matroska,webm, from '1472343215-10.webm':
     Metadata:
       encoder         : Lavf56.40.101
     Duration: 00:00:05.03, start: 0.000000, bitrate: 80 kb/s
       Stream #0:0(eng): Video: vp8, yuv420p, 1280x760, SAR 1:1 DAR 32:19, 30 fps, 30 tbr, 1k tbn, 1k tbc (default)
    [webm @ 0x1349940] Codec for stream 0 does not use global headers but container format requires global headers
    Output #0, webm, to '1472343215-10.webm':
     Metadata:
       encoder         : Lavf56.40.101
       Stream #0:0(eng): Video: vp8, yuv420p, 1280x760 [SAR 1:1 DAR 32:19], q=2-31, 30 fps, 30 tbr, 1k tbn, 1k tbc (default)
    Stream mapping:
     Stream #0:0 -> #0:0 (copy)
  • FFMPEG throws error while converting WEBM/MP4 audio files from MediaRecorder to MP3

    27 septembre 2022, par Sahil Malik

    I use MediaRecorder to record the audio from the browser and then upload it to my server (ARM-based Linux machine - AWS Lambda Function, if it matters). Based on the browser type, I get either an MP4 file (for the Safari browser) OR a WEBM file (for every other browser) from the MediaRecorder. The audio is converted to Base64 string and posted to my server with FFMPEG.

    &#xA;

    MediaRecorder implementation

    &#xA;

    const audioStream = await navigator.mediaDevices.getUserMedia(mediaConstraints);&#xA;const MediaRecorder = window[&#x27;MediaRecorder&#x27;];&#xA;const mimeType = MediaRecorder.isTypeSupported(&#x27;audio/webm&#x27;) ? &#x27;audio/webm&#x27; : &#x27;audio/mp4&#x27;;&#xA;const recordingFileExtension = MediaRecorder.isTypeSupported(&#x27;audio/webm&#x27;) ? &#x27;webm&#x27; : &#x27;mp4&#x27;;&#xA;mediaRecorder = new MediaRecorder(audioStream, { mimeType });&#xA;mediaRecorder.ondataavailable = convertBlobAndUploadChunk;&#xA;mediaRecorder.start(30 * 1000); // timeslice needs to be in ms&#xA;// When user stops recording&#xA;mediaRecorder.stop();&#xA;audioStream.getTracks().forEach( t => { t.stop(); });&#xA;// To convert the audio blob to string&#xA;function convertBlobToBase64(blob) {&#xA;    return new Promise((resolve, reject) => {&#xA;        const reader = new FileReader();&#xA;        reader.readAsDataURL(blob);&#xA;        reader.onload = () => {&#xA;            const tmpStr = reader.result.toString();&#xA;            resolve(tmpStr.substring(tmpStr.indexOf(&#x27;base64,&#x27;) &#x2B; 7));&#xA;        };&#xA;        reader.onerror = error => reject(error);&#xA;    });&#xA;}&#xA;&#xA;let blobCount = 0;&#xA;async function convertBlobAndUploadChunk(blobEvent) {&#xA;    if (!blobEvent.data || blobEvent.data.size === 0) return;&#xA;&#xA;    blobCount&#x2B;&#x2B;;&#xA;&#xA;    const recordData = JSON.stringify({&#xA;        M: {&#xA;            blobCount,&#xA;            //some other meta data&#xA;        },&#xA;        D: await convertBlobToBase64(audioBlob),&#xA;    });&#xA;&#xA;    await angularHttpClient.post(apiUrl, recordData, new HttpHeaders({&#xA;        &#x27;Content-Type&#x27;: &#x27;application/json&#x27;,&#xA;        &#x27;x-api-key&#x27;: apiKey,&#xA;    })).toPromise();&#xA;}&#xA;

    &#xA;

    On my server, I convert the WEBM/MP4 file to an MP3 file for better cross-browser compatibility and to enable scrubbing.

    &#xA;

    Backend Lambda Implementation

    &#xA;

    const FFMpegCommand = require(&#x27;fluent-ffmpeg&#x27;); // v2.1.2&#xA;new FFMpegCommand()&#xA;        .input(originalFile)&#xA;        .on(&#x27;end&#x27;, (error, stdOut, stdError) => {&#xA;            if (error) {&#xA;                console.error(error);&#xA;            }&#xA;        })&#xA;        .save(convertedFile);&#xA;

    &#xA;

    This works perfectly fine 99.9% of the time but FFMPEG throws one of the following errors for 0.1% of the time :

    &#xA;

    Error 1 : Invalid data found when processing input

    &#xA;

    error reading header

    &#xA;

    ffmpeg version 4.4-static https://johnvansickle.com/ffmpeg/ Copyright (c) 2000-2021 the &#xA;FFmpeg developers&#xA;built with gcc 8 (Debian 8.3.0-6)&#xA;configuration: --enable-gpl --enable-version3 --enable-static --disable-debug --disable-ffplay --disable-indev=sndio --disable-outdev=sndio --cc=gcc --enable-fontconfig --enable-frei0r --enable-gnutls --enable-gmp --enable-libgme --enable-gray --enable-libaom --enable-libfribidi --enable-libass --enable-libvmaf --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-librubberband --enable-libsoxr --enable-libspeex --enable-libsrt --enable-libvorbis --enable-libopus --enable-libtheora --enable-libvidstab --enable-libvo-amrwbenc --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libdav1d --enable-libxvid --enable-libzvbi --enable-libzimg&#xA;libavutil 56. 70.100 / 56. 70.100&#xA;libavcodec 58.134.100 / 58.134.100&#xA;libavformat 58. 76.100 / 58. 76.100&#xA;libavdevice 58. 13.100 / 58. 13.100&#xA;libavfilter 7.110.100 / 7.110.100&#xA;libswscale 5. 9.100 / 5. 9.100&#xA;libswresample 3. 9.100 / 3. 9.100&#xA;libpostproc 55. 9.100 / 55. 9.100&#xA;[mov,mp4,m4a,3gp,3g2,mj2 @ 0x7232f40] could not find corresponding trex (id 1)&#xA;[mov,mp4,m4a,3gp,3g2,mj2 @ 0x7232f40] could not find corresponding track id 0&#xA;[mov,mp4,m4a,3gp,3g2,mj2 @ 0x7232f40] trun track id unknown, no tfhd was found&#xA;[mov,mp4,m4a,3gp,3g2,mj2 @ 0x7232f40] error reading header&#xA;/tmp/long-filename-of-140chars-to-keep-files-unique.mp4: Invalid data found when processing input&#xA;

    &#xA;

    EBML Header parsing failed

    &#xA;

    ffmpeg version 4.4-static https://johnvansickle.com/ffmpeg/ Copyright (c) 2000-2021 the FFmpeg developers&#xA;built with gcc 8 (Debian 8.3.0-6)&#xA;configuration: --enable-gpl --enable-version3 --enable-static --disable-debug --disable-ffplay --disable-indev=sndio --disable-outdev=sndio --cc=gcc --enable-fontconfig --enable-frei0r --enable-gnutls --enable-gmp --enable-libgme --enable-gray --enable-libaom --enable-libfribidi --enable-libass --enable-libvmaf --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-librubberband --enable-libsoxr --enable-libspeex --enable-libsrt --enable-libvorbis --enable-libopus --enable-libtheora --enable-libvidstab --enable-libvo-amrwbenc --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libdav1d --enable-libxvid --enable-libzvbi --enable-libzimg&#xA;libavutil 56. 70.100 / 56. 70.100&#xA;libavcodec 58.134.100 / 58.134.100&#xA;libavformat 58. 76.100 / 58. 76.100&#xA;libavdevice 58. 13.100 / 58. 13.100&#xA;libavfilter 7.110.100 / 7.110.100&#xA;libswscale 5. 9.100 / 5. 9.100&#xA;libswresample 3. 9.100 / 3. 9.100&#xA;libpostproc 55. 9.100 / 55. 9.100&#xA;[matroska,webm @ 0x7595f40] Format matroska,webm detected only with low score of 1, misdetection possible!&#xA;[matroska,webm @ 0x7595f40] EBML header parsing failed&#xA;/tmp/long-filename-of-140chars-to-keep-files-unique.webm: Invalid data found when processing input&#xA;

    &#xA;

    No specific details

    &#xA;

    ffmpeg version 4.4-static https://johnvansickle.com/ffmpeg/ Copyright (c) 2000-2021 the FFmpeg developers&#xA;built with gcc 8 (Debian 8.3.0-6)&#xA;configuration: --enable-gpl --enable-version3 --enable-static --disable-debug --disable-ffplay --disable-indev=sndio --disable-outdev=sndio --cc=gcc --enable-fontconfig --enable-frei0r --enable-gnutls --enable-gmp --enable-libgme --enable-gray --enable-libaom --enable-libfribidi --enable-libass --enable-libvmaf --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-librubberband --enable-libsoxr --enable-libspeex --enable-libsrt --enable-libvorbis --enable-libopus --enable-libtheora --enable-libvidstab --enable-libvo-amrwbenc --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libdav1d --enable-libxvid --enable-libzvbi --enable-libzimg&#xA;libavutil 56. 70.100 / 56. 70.100&#xA;libavcodec 58.134.100 / 58.134.100&#xA;libavformat 58. 76.100 / 58. 76.100&#xA;libavdevice 58. 13.100 / 58. 13.100&#xA;libavfilter 7.110.100 / 7.110.100&#xA;libswscale 5. 9.100 / 5. 9.100&#xA;libswresample 3. 9.100 / 3. 9.100&#xA;libpostproc 55. 9.100 / 55. 9.100&#xA;/tmp/long-filename-of-140chars-to-keep-files-unique.webm: Invalid data found when processing input&#xA;

    &#xA;

    Error 2 : Output file #0 does not contain any stream

    &#xA;

    ffmpeg version 4.4-static https://johnvansickle.com/ffmpeg/ Copyright (c) 2000-2021 the FFmpeg developers&#xA;built with gcc 8 (Debian 8.3.0-6)&#xA;configuration: --enable-gpl --enable-version3 --enable-static --disable-debug --disable-ffplay --disable-indev=sndio --disable-outdev=sndio --cc=gcc --enable-fontconfig --enable-frei0r --enable-gnutls --enable-gmp --enable-libgme --enable-gray --enable-libaom --enable-libfribidi --enable-libass --enable-libvmaf --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-librubberband --enable-libsoxr --enable-libspeex --enable-libsrt --enable-libvorbis --enable-libopus --enable-libtheora --enable-libvidstab --enable-libvo-amrwbenc --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libdav1d --enable-libxvid --enable-libzvbi --enable-libzimg&#xA;libavutil 56. 70.100 / 56. 70.100&#xA;libavcodec 58.134.100 / 58.134.100&#xA;libavformat 58. 76.100 / 58. 76.100&#xA;libavdevice 58. 13.100 / 58. 13.100&#xA;libavfilter 7.110.100 / 7.110.100&#xA;libswscale 5. 9.100 / 5. 9.100&#xA;libswresample 3. 9.100 / 3. 9.100&#xA;libpostproc 55. 9.100 / 55. 9.100&#xA;[mpegts @ 0x5d19f40] Format mpegts detected only with low score of 2, misdetection possible!&#xA;[mpegts @ 0x5d19f40] Could not detect TS packet size, defaulting to non-FEC/DVHS&#xA;Input #0, mpegts, from &#x27;/tmp/long-filename-of-140chars-to-keep-files-unique.webm&#x27;:&#xA;Duration: N/A, bitrate: N/A&#xA;Output #0, mp3, to &#x27;/tmp/long-filename-of-140chars-to-keep-files-unique.mp3&#x27;:&#xA;Output file #0 does not contain any stream&#xA;

    &#xA;

    Thanks for reading, any help/suggestion is highly appreciated.

    &#xA;