Pipe Platform Documentation

Embedding the Recording Client


Embedding your first recorder

To embed your first recorder in your website, follow these steps:

  1. Sign in to your Pipe account dashboard and navigate to the Embed Recorder section.
  2. In the lower right area, you will find the 2.0 and 1.0 embed codes. We'll use the 2.0 HTML code. The 2.0 HTML code consists of two parts that need to be copied separately.
  3. Open the HTML page where you want to embed the recorder in a text/code editor.
  4. Paste the first part of the 2.0 HTML code in the <head> section of your HTML page. This part triggers the loading of the required JavaScript and CSS files.
  5. Paste the second part of the 2.0 HTML code at the point in your HTML page where you want the actual recorder interface to be displayed.
  6. Save the modified HTML page and upload it back to your website.
  7. Once the modified page is uploaded, refresh the page in your browser to see the embedded recorder in action.

After the Pipe Recording Client script (pipe.js) and the rest of the page elements load (DOMContentLoaded), pipe.js will search for any <piperecorder...> tag on the page and, inside the tag, will insert the HTML code for the recorder. At the end it will fire PipeSDK.onRecordersInserted() (if present).

This variant of loading pipe.js will block HTML parsing until the script is loaded and executed. In some cases (slow connections) this is not optimal. For more info on how to load pipe.js in a non-blocking way see the Making the script non-blocking section below.

Any JS events and control API implementation that relies on PipeSDK.onRecordersInserted() needs to be placed AFTER the pipe.js script tag in the page. Otherwise, you'll get an Uncaught ReferenceError: PipeSDK is not defined error in the browser console.

Loading pipe.js in a non-blocking way

Putting the <script> tag at the top of the page will block HTML parsing until pipe.js is loaded. This is true for all external JS scripts loaded in this manner. This is undesirable in some cases:

  1. Over slow connections: the page will take longer to start rendering because it has to download and execute pipe.js first. The slower the connection, the longer page rendering will be delayed.
  2. If there are any issues with accessing pipe.js: one might have to wait for the request to complete before page rendering will start.

There are two ways to load pipe.js in a non-blocking way. We will explore each variant below and why these variants are not the default recommendation.

Placing the <script> tag at the bottom of the body

To prevent the browser from blocking the HTML page from parsing until pipe.js is loaded and executed, you can place the <script> tag at the bottom of the page just before the ending </body> tag. pipe.js will be loaded and executed at the end without delaying any page element above them. The main page content is shown before our assets start loading.

Make sure to place any JS Events API and JS Control API code after the <script> code for inserting pipe.js in your page, including code that depends on PipeSDK.onRecordersInserted(). This way, you ensure that the necessary dependencies, such as the PipeSDK object, are loaded before executing any code that relies on it. Failure to do so will result in an Uncaught ReferenceError: PipeSDK is not defined error in the browser console.

This variant does work with the 2.0 JS embed code, but the pipe.js script needs to be loaded before any instruction is sent via the PipeSDK object.

Adding the defer attribute

Another option is to include the defer attribute in the script tag that references pipe.js. By adding this attribute, the loading is triggered right away but happens asynchronously (without blocking HTML parsing), and the execution of the script is deferred until after the page has completed parsing.

This approach allows the main content to be presented to visitors swiftly, just as placing the script at the bottom of the body tag. This variant is slightly faster because pipe.js loads in parallel. However, any kind of JS Events or JS Control API code (including any mention of PipeSDK.onRecordersInserted() or PipeSDK.insert()) needs to be initialized after window.onload triggers, as in the following 2.0 HTML example.

<html>
<head>
  <link rel="stylesheet" href="https://cdn.addpipe.com/2.0/pipe.css">
  <script defer type="text/javascript" src="https://cdn.addpipe.com/2.0/pipe.js"></script>
</head>
<body>
<piperecorder id="custom-id" pipe-width="640" pipe-height="390" pipe-qualityurl="avq/360p.xml" pipe-accounthash="1edfef4c4967d69b7129ec3ffa534002" pipe-eid="j2HCiP" pipe-mrt="600" pipe-avrec="1"></piperecorder>
<input type="button" class="btn" value="Record" id="recordbtn" />
<script>
function initializePipeSDK() {
  PipeSDK.onRecordersInserted = function(){
    myRecorder =  PipeSDK.getRecorderById('custom-id');
    myRecorder.onReadyToRecord = function(id, type){
    console.log('onReadyToRecord')
      document.getElementById("recordbtn").onclick = function (){
        myRecorder.record();
      }
    }
  }
}
window.onload = initializePipeSDK;
</script>
</body>
</html>

For a more detailed explanation of how placing the <script> tag at the end and using the defer atrribute affect script loading and html page parsing check out this article.

Two versions of the embed code

Pipe has 2 versions of the embed code. The 1.0 version is the initial one used since launch and the 2.0 version is the new one which eliminates some of the limitations of the 1.0 embed code.

Embed Code v1.0

This is the initial embed code used since launching the Pipe Platform in 2016.

We now consider it rather outdated since it uses script injection, global JS vars, and global functions for the JS Events API, it does not support multiple recorders on the same page and it's tricky to use with React, Angular, or Vue.js.

Many of our clients use it, but we encourage everyone to move to the 2.0 embed code. Eventually, we'll retire the 1.0 embed code.

Embed Code v2.0

The 2.0 embed code allows for multiple recorders on the same page, uses an external CSS file for styling, makes fewer HTTP requests, allows you to insert and remove recorders from the page programmatically, has a better JS Control & Events API, and uses high DPI icons. You can read more about it in the 2.0 embed code release blog post.

The 2.0 embed code comes in 2 flavors: HTML and JavaScript.

  1. The 2.0 HTML embed code in particular relies on HTML tags for easy integration with any HTML page. It is now the default embed code.
  2. The 2.0 Java Script embed code in particular makes it easy to use Pipe with JS-heavy single-page web apps like those built using React, Angular, or Vue.js. You can also use it to dynamically insert, remove, and control recorders on the page. It starts by loading pipe.js so that it is loaded and ready whenever you want to add a recorder.

Adding multiple recorders to the page

One of the new things you can do with the new v2.0 embed code is to add multiple recorders to the HTML page.

v2.0 HTML

To do this with the v2.0 HTML embed code you must simply add multiple <piperecorder...> tags to your HTML page, with unique IDs, one for each recorder you want on the page. You can change the attributes for each recorder to fit your needs.

Here's an example with 3 recorders of different sizes, different recording resolutions, and different max recording times:

<head>
    <link rel="stylesheet" href="//cdn.addpipe.com/2.0/pipe.css"/>
    <script type="text/javascript" src = "//cdn.addpipe.com/2.0/pipe.js"></script>
<head/>
<body>
    <piperecorder id="tag-recorder1" pipe-width="640" pipe-height="510" pipe-qualityurl="avq/480p.xml" pipe-accounthash="ACCOUNT_HASH" pipe-eid="1" pipe-showmenu="1" pipe-mrt="60" pipe-sis="0" pipe-asv="0" pipe-mv="1" pipe-st="1" pipe-ssb="1" pipe-dpv="0" pipe-ao="0" pipe-dup="1" pipe-cornerradius="8" pipe-bgcol="0xf6f6f6" pipe-menucol="0xe9e9e9" pipe-normalcol="0x334455" pipe-overcol="0x556677" pipe-payload='{"userId":"55a95eeb936dd30100e0aff6","jobId":"55a7e6555f1bdc010014d6a1"}'></piperecorder>
    <piperecorder id="tag-recorder2" pipe-width="1280" pipe-height="720" pipe-qualityurl="avq/720p.xml" pipe-accounthash="ACCOUNT_HASH" pipe-eid="1" pipe-showmenu="1" pipe-mrt="120" pipe-sis="0" pipe-asv="0" pipe-mv="1" pipe-st="1" pipe-ssb="1" pipe-dpv="0" pipe-ao="0" pipe-dup="1" pipe-cornerradius="8" pipe-bgcol="0xf6f6f6" pipe-menucol="0xe9e9e9" pipe-normalcol="0x334455" pipe-overcol="0x556677" pipe-payload='{"userId":"55a95eeb936dd30100e0aff6","jobId":"55a7e6555f1bdc010014d6a1"}'></piperecorder>
    <piperecorder id="tag-recorder3" pipe-width="80%" pipe-height="240" pipe-qualityurl="avq/240p.xml" pipe-accounthash="ACCOUNT_HASH" pipe-eid="1" pipe-showmenu="1" pipe-mrt="180" pipe-sis="0" pipe-asv="0" pipe-mv="1" pipe-st="1" pipe-ssb="1" pipe-dpv="0" pipe-ao="0" pipe-dup="1" pipe-cornerradius="8" pipe-bgcol="0xf6f6f6" pipe-menucol="0xe9e9e9" pipe-normalcol="0x334455" pipe-overcol="0x556677" pipe-payload='{"userId":"55a95eeb936dd30100e0aff6","jobId":"55a7e6555f1bdc010014d6a1"}'></piperecorder>
<body/>

You can move the insertion of pipe.js and pipe.css to the bottom of the page (after the <piperecorder...> tags) to avoid the page not rendering until pipe.js and pipe.css are loaded.

After pipe.js and the rest of the page elements load (DOMContentLoaded), pipe.js will automatically insert the recorder HTML code inside the piperecorder tags and fire PipeSDK.onRecordersInserted() if present.

Any JS events and control API implementation that relies on PipeSDK.onRecordersInserted() needs to be placed AFTER the code for inserting pipe.js in the page. Otherwise, you'll get an Uncaught ReferenceError: PipeSDK is not defined error in the browser console.

v2.0 JavaScript

To add multiple recorders to the page using the 2.0 JavaScript embed code just insert pipe.css and pipe.js in the page and use PipeSDK.insert("div-id",config-object) every time you want to insert a recorder.

Here's for example how to insert 3 video recorders on the page. Each will replace a different DIV and have a different size (as specified in the config object).

<head>
    <link rel="stylesheet" href="//cdn.addpipe.com/2.0/pipe.css"/>
    <script type="text/javascript" src = "//cdn.addpipe.com/2.0/pipe.js"></script>
<head/>
<body>
    <div id="custom-id1" ></div>
    <div id="custom-id2" ></div>
    <div id="custom-id3" ></div>

    <script type="text/javascript">
    var pipeParams1 = {size: {width:400,height:330}, qualityurl: "avq/360p.xml",accountHash:"ACCOUNT_HASH", eid:1, showMenu:"true", mrt:60,sis:0,asv:1,mv:0, dpv:0, ao:0, dup:0};
    var pipeParams2 = {size: {width:640,height:430}, qualityurl: "avq/480p.xml",accountHash:"ACCOUNT_HASH", eid:1, showMenu:"true", mrt:120,sis:0,asv:1,mv:0, dpv:0, ao:0, dup:0};
    var pipeParams3 = {size: {width:"80%",height:750}, qualityurl: "avq/720p.xml",accountHash:"ACCOUNT_HASH", eid:1, showMenu:"true", mrt:180,sis:0,asv:1,mv:0, dpv:0, ao:0, dup:0};

    PipeSDK.insert('custom-id1', pipeParams1, function(myRecorderObject){ });
    PipeSDK.insert('custom-id2', pipeParams2, function(myRecorderObject){ });
    PipeSDK.insert('custom-id3', pipeParams3, function(myRecorderObject){ });
    </script>
<body/>

You can move the insertion of pipe.js and pipe.css to the bottom of the page to avoid the page not rendering until pipe.js and pipe.css have loaded, but any code involving PipeSDK will have to be made after the code that inserts pipe.js in the page.

Using the minified version of pipe.js

The minified version, pipe.min.js, is a more compact version of pipe.js. It is smaller, so it will load faster for your website users. This is especially helpful to users who have a slow Internet connection.

As I write this, the standard version is 92.88 KB after gzip compression, and the minified version is 82.29 KB ( roughly 11,5% smaller ).

The functionality remains the same but with the added benefits of a smaller JavaScript file.

To switch to the minified version, replace pipe.js with pipe.min.js in your <script> tag:

  • when loading from our AWS powered worldwide CDN: <script type="text/javascript" src="https://cdn.addpipe.com/2.0/pipe.min.js"></script>
  • when loading from our EU delivery servers: <script type="text/javascript" src="https://s1.addpipe.com/2.0/pipe.min.js"></script>

Viewing recordings on your account


Once a recording is made it will show up in your Pipe account dashboard. You can see all the recordings for an environment on your account by clicking on the Recordings tab.

Here's what the list of recordings looks like: List of all videos in pipe account area

Every recording appears on a table row along with useful information grouped in 6 columns:

ID/Date/Time/IP The id of the recording as it is stored in our database, the date and time when the recording was made, and the IP of the device from which it was streamed or uploaded
Source The source (Computer or Mobile) along with the browser and OS, microphone and camera names, file name, format, original size, and the payload attached to the recorder
Transcoded Details about the transcoded output files including public links to where they're stored by us (if the option is turned on) and details about the main .mp4 file
Thumbnail The thumbnail of the recording snapshot (if it is a video recording or a mic icon for audio-only recordings)
Status The status of all the processes in the pipeline (recording, transcoding, storing, etc.)
Action Buttons for embedding the recording, downloading it, and for pushing it to YouTube.

Requirements and supported browsers


Pipe officially supports the following browsers.

💻 On Desktop Devices:

  • Desktop HTML5 recorder
    • webcam recording: Chrome 63+, Firefox 50+ and Edge 79+ on secure origins (https and localhost)
    • screen recording: Chrome 72+, Firefox 66+ and Edge 79+ on secure origins (https and localhost)

📱 On Mobile Devices:

  • Mobile native recorder:
    • Chrome on Android version 4.4 and up
    • Safari on iOS 6 and up
  • Desktop HTML 5 recorder:
    • Not supported, use the 2.0 embed code instead

💻 On Desktop Devices:

  • Desktop HTML5 recorder
    • webcam recording: Chrome 63+, Firefox 50+, Edge 79+ and Safari 14.0.2+Beta on secure origins (https and localhost)
    • screen recording: Chrome 74+, Firefox 66+, Edge 79+ and Safari 14.0.2+Beta on secure origins (https and localhost)

📱 On Mobile Devices:

  • Mobile native recorder:
    • Chrome on Android version 4.4 and up
    • Safari on iOS 6 and up
  • Desktop HTML 5 recorder:
    • Chrome 63+ on secure origins (https and localhost) on Android
    • Safari on iOS/iPadOS 14.3+Beta on secure origins (https and localhost)

Changing the recorder size


The space occupied by the recorder client differs between the desktop recorder and mobile native recorder because the technology used is different and thus the UI is different.

Desktop recorder size

The size of the desktop recorder can be changed directly from the embed code or, when generating new embed codes, from our embed panel as shown below.

Changing when generating new embed codes

In your Pipe account dashboard, under the "Embed Recorder" tab, you will find the following panel with the width and height options:

Embeded recorder menu where you can select the recorder size

Use the 2 inputs to specify the width and height of the black video area inside the recorder.

The width value you specify can also contain the CSS unit of length. You can use any valid CSS unit. If no unit is provided, our JS code will assume the value is in px . Valid values: 360, 360px, 50%, 100vm . In case an invalid CSS unit is specified for the width, we’ll just use the numeric part and pixels as the CSS unit.

The height value you specify here does not include the bottom control menu which will add 30px to the height. If you set a 320x240 size for the video area and keep the bottom menu you'll end up with a 320x270 recorder in pages opened on desktop devices.

In case an unsupported size unit is specified for the width, the value will default to pixels.

After you enter your desired size, press the "Generate Embed Code" button to update the preview and the size values in the embed code to the right of the page.

The size of the recorder won't affect the resolution & quality of the videos recorded.

Changing directly in the embed code

If you have a recorder already embedded in your web pages or if you're in the editor and you want to do a quick change you can easily change the size of the desktop recorder.

In the 2.0 HTML embed code the size of the recorder is specified through the pipe-width and pipe-height attributes:

<piperecorder... pipe-width="400" pipe-height="330" ... >

The height value includes the 30px bottom menu so if you want a 400x300 video area in your desktop recorder ( to avoid black bars when recording at 400x300 or 4:3 video ) use 400 for the pipe-width and 330 for the pipe-height attributes.

For the width value, the measurement unit can also be specified, for example pipe-width can be 80%, 50vw, 250px, and so on. If no unit is specified in the pipe-width value, the value will be assumed to be in px

Here is an example where the width unit is %:

<piperecorder... pipe-width="80%" pipe-height="330" ... >

In the 2.0 JS embed code the size of the recorder is specified through the width and height properties of the size object:

var pipeParams = {size: {width:320,height:270}, qualityurl: "avq/360p.xml", accountHash:"1edfef4c4967d69b7129ec3ffa534002"};

The height value includes the 30px bottom menu so if you want a 320x240 video area in your desktop recorder ( to avoid black bars when recording 320x240 or 4:3 video ) use 320x270 in the size object.

The width value is, by default, a number (width:320) but can be specified as a string to include the CSS unit (width:“80%“). If it is added as a number or string without a measurement unit, the value will be assumed to be in px.

Here is an example where the width unit is %:

var pipeParams = {size: {width:"80%",height:270}, qualityurl: "avq/360p.xml", accountHash:"1edfef4c4967d69b7129ec3ffa534002"};

In the 1.0 embed code the size of the recorder is specified through the size object. You'll see this line in every 1.0 Pipe embed code:

var size = {width:400,height:330};

The height value includes the 30px bottom menu so if you want a 320x240 video area in your desktop recorder ( to avoid black bars when recording 320x240 or 4:3 video ) use 320x270 in the size object.

Mobile native recorder size

With the 2.0 embed code, we've moved from using 2 buttons to only 1 button for the mobile UI. Here's the recording process from beginning to end: Pipe mobile recorder UI on iOS when using 2.0 embed code

Height

Since we're only using one button the height is reduced considerably to just around 40px excluding the vertical 10px margins. The height on your website might differ depending on the font face and size of the CSS styles inherited.

Width

The width of the button adjusts to the text label inside it. If the text is too long it will wrap to the next line while remaining center aligned.

Container DIV

The container DIV holding the button is 100% width with a 10px top and bottom margin and it uses the pipeMobileRecorder CSS class which you can overwrite in your CSS.

Here's the div highlighted on a mobile web page with Chrome's Developer Tools:
Container div highlighted with Chrome's Developer Tools on pipe mobile UI on 2.0-embed code

Button

The button is a label element with the pipeFileInput CSS class applied which defaults to:

  1. a vertical linear grey gradient
  2. 1px solid #999 border
  3. 10px padding all around and 3px rounded corner
  4. it inherits whatever fonts and font sizes are used in your website's CSS
  5. the width of the button adjusts to the text label inside it, if the text is too long it will wrap to the next line while remaining center aligned

You can overwrite the pipeFileInput CSS class in your CSS.

The 1.0 embed code creates 2 buttons that can be used to record and upload a video as shown in this sequence showing the demo on our homepage running in Safari on iOS (click on it for full size):Pipe mobile recorder UI on iOS

The 2 buttons are included in a div with the id hdfvr-content. The width of this div is 100% and the height is fixed at 120px. Here's the div highlighted on a mobile web page with Chrome's Developer Tools
Div with the id hdfvr-content highlighted with Chrome's Developer Tools

Colors and rounded corners


Desktop recorder

Pipe's desktop recorder can be customized in terms of size (see above), colors, and square/rounded corners. These styling options are available regardless of the embed code used (1.0 or 2.0).

Colors & rounded corners

You can change the colors of the desktop recorder to better fit your website design.

These can be changed from your account area for new recorders/embed codes or from the embed code directly.

Changing from the Pipe account dashboard for new embed codes

To change the colors from your account area, you will have to open the design options panel:

Opening the design options from panel

From here you will be able to change from top to bottom:

  1. the radius of the 4 corners of the recorder
  2. the background color showed in the initial screen
  3. the bottom menu background color
  4. the normal and muse over colors for the text buttons in the initial screen
Pipe customization menu for colors and rounded corners

Once you're done, press the "Generate Embed Code" button to see the changes and get the new embed code that includes these changes.

Changing in existing embed codes

For existing embed codes that you have in your website or blog posts, you can edit the code directly. Here's what you have to do depending on your embed code version and type:

Change the value of the cornerradius, bgCol, menuCol, normalCol, and overCol properties of the flashvars object in your embed code.

var flashvars = {qualityurl: "avq/360p.xml",accountHash:"1edfef4c4967d69b7129ec3ffa534002", eid:1, showMenu:"false", mrt:120,sis:0,asv:1,mv:0, dpv:0, ao:0, dup:0, cornerradius:40, bgCol:"0xff0000", menuCol:"0xff00e3", normalCol:"0x00ff77", overCol:"0xffda00"};

Change the value of the pipe-cornerradius, pipe-bgcol, pipe-menucol, pipe-normalcol, and pipe-overcol attributes of the piperecorder tag.

<piperecorder id="custom-id" pipe-width="400" pipe-height="300" pipe-qualityurl="avq/360p.xml" pipe-accounthash="1edfef4c4967d69b7129ec3ffa534002" pipe-eid="1" pipe-showmenu="false" pipe-mrt="120" pipe-sis="0" pipe-asv="1" pipe-mv="0" pipe-dpv="0" pipe-ao="0" pipe-dup="0" pipe-cornerradius="40" pipe-bgcol="0xff0000" pipe-menucol="0xff00e3" pipe-normalcol="0x00ff77" pipe-overcol="0xffda00" ></piperecorder>

Change the value of the cornerradius, bgCol, menuCol, normalCol, and overCol properties of your JavaScript config object in your embed code.

var pipeParams = {size: {width:400,height:300}, qualityurl: "avq/360p.xml",accountHash:"1edfef4c4967d69b7129ec3ffa534002", eid:1, showMenu:"false", mrt:120,sis:0,asv:1,mv:0, dpv:0, ao:0, dup:0, cornerradius:40, bgCol:"0xff0000", menuCol:"0xff00e3", normalCol:"0x00ff77", overCol:"0xffda00"};

Mobile native recorder

Pipe's mobile native recorder can be customized in terms of size (see above) and colors.

Since the mobile recorder is made of 1 or 2 HTML buttons you can easily overwrite the CSS classes that apply to those elements.

1.0 embed code

When using the 1.0 embed code the user is presented with 2 buttons. As soon as the user clicks the top button the OS takes over.

Safari on iOS

Mobile preview of pipe 1.0 embeded code on Safari on iOS

Chrome on Android

Mobile preview of pipe 1.0 embeded code on Chrome on Android

2.0 embed code

When using the 2.0 embed code the user is only presented with one button. As soon as the user clicks the button the OS takes over.

For the CSS classes used by the 2.0 embed code for the recorder button shown on mobile browsers see below.

Hiding the bottom control menu


You can opt to hide the lower control menu of the desktop recorder. You may want to do this if you decide to control the recorder using the JavaScript Control API (embed code v1.0 or embed code v2.0). The menu has a height of 30px.

When generating an embed code from the Pipe account dashboard you can hide the menu by unchecking the option shown in the image below:

Option to display/show bottom menu

Here's how the newer HTML5 recorder looks without the bottom menu:

Pipe desktop recorder without a bottom menu

Limiting the length of recordings


Desktop recorder

Recordings from the desktop recorder made on the spot using a webcam and/or microphone can be limited in length. Once the length is hit, the recorder stops the recording as if the STOP button has been clicked.

New embed codes

When generating a new embed code from the Pipe account dashboard you can easily change the default length (120 seconds) by editing the Max Recording Time value and pressing the green Generate Embed Code.

The new embed code will create a recorder that will limit the length of the recordings recorded on the spot with the desktop recorder to your specified length.

The value is in seconds.

Setting the max recording time in the options menu

Existing embed codes

For existing embed codes that you have in your website or blog posts, you can edit the code directly. Here's what you have to do depending on your embed code version and type:

Change the value of the mrt property of the flashvars object in your embed code. The default value is 600 seconds.

var flashvars = {qualityurl: "avq/360p.xml",accountHash:"1edfef4c4967d69b7129ec3ffa534002", eid:1, showMenu:"true", mrt:600,sis:0,asv:1,mv:0, dpv:0, ao:0, dup:0};

Change the value of the pipe-mrt attribute of the piperecorder tag. The default value is 600 seconds.

<piperecorder id="custom-id" pipe-width="400" pipe-height="330" pipe-qualityurl="avq/360p.xml" pipe-accounthash="1edfef4c4967d69b7129ec3ffa534002" pipe-eid="1" pipe-showmenu="true" pipe-mrt="600" pipe-sis="0" pipe-asv="1" pipe-mv="0" pipe-dpv="0" pipe-ao="0" pipe-dup="0" ></piperecorder>

Change the value of the mrt property of your JavaScript config object in your embed code. The default value is 600 seconds.

var pipeParams = {size: {width:400,height:330}, qualityurl: "avq/360p.xml",accountHash:"1edfef4c4967d69b7129ec3ffa534002", eid:1, showMenu:"true", mrt:600,sis:0,asv:1,mv:0, dpv:0, ao:0, dup:0};

Mobile native recorder

When recording from mobile browsers like Safari on iOS and Chrome on Android with the mobile native recorder we can not control the length of the recorded video. The HTML Media Capture standard we're using relies on the OS capabilities to handle the video recording and it does not have the option to limit the length.

Cut recordings from mobile native recorders (iOS and Android) to length

However, in Pipe, there is an option to cut mobile recordings to the max length specified in the embed code if the recording made with the mobile native recorder is longer than the maximum allowed length.

The option can be found in the Transcoding Engine section.

When relying on this mechanism you should advise your mobile users that their recordings will be cut to length.

You will only be charged for the processed portion of the recording, you will not be charged for the entire length.

Allowing file uploads from the desktop recorder


By default, with the desktop recorder, users can only record a new video or audio file using their webcam and/or microphone.

You can allow users to upload an existing audio or video file they have on their desktop device by turning on the file upload feature in the embed code.

Once the feature is enabled in the embed code, the desktop recorder will show a new option to upload an existing recording:
Pipe recorder with file uploads enabled

There is currently a max size limit of 5 GiB (5*1024*1024*1024 bytes). If the user tries to upload a larger file, the file will not be accepted and a message will be shown to the user.

New embed codes

For new embed codes, you can turn on the feature from the Pipe account dashboard. Activate the checkbox highlighted in the image below and press the green Generate Embed Code. Now copy and paste that embed code in your website.

Allowing file uploads in the options menu

Existing embed codes

For existing embed codes that you have in your website or blog posts, you can edit the code directly. Here's what you have to do depending on your embed code version and type:

Make sure the dup property of the flashvars object is set to 1 like this:Code to enable desktop file uploads in pipe video recorder in JavaScript

If the property is missing, add it.

Make sure the pipe-dup attribute of the piperecorder tag is set to 1 like this:

<piperecorder id="custom-id" pipe-width="400" pipe-height="330" pipe-qualityurl="avq/360p.xml" pipe-accounthash="1edfef4c4967d69b7129ec3ffa534002" pipe-eid="1" pipe-showmenu="true" pipe-mrt="120" pipe-sis="0" pipe-asv="1" pipe-mv="0" pipe-dpv="0" pipe-ao="0" pipe-dup="1" ></piperecorder>

If the attribute is missing, add it.

Make sure the dup property is set to 1 in your JavaScript config object like this:

var pipeParams = {size: {width:400,height:330}, qualityurl: "avq/360p.xml",accountHash:"1edfef4c4967d69b7129ec3ffa534002", eid:1, showMenu:"true", mrt:120, sis:0, asv:1, mv:0, dpv:0, ao:0, dup:1};

If the property is missing, add it.

Supported file types (containers)

We’re currently allowing users to upload the following video & audio file types:

VideoAudio
mp4
mov
webm
3gp
3gpp
3g2
flv
avi
m4v
ogv
mod
qt
wmv
mpg
mpeg
aac
m4a
mp3
wav
ogg
wma
amr
flac

When audio only is turned ON (ao:1) users will only be allowed to upload audio files.

Audio and video quality


With Pipe, the amount of control over video quality depends on the recording client being used. With the HTML5 desktop recorder, you have granular control over the resolution. With the mobile native recorder, the resolution, audio, and video quality depend on the device and the settings on the device.

Desktop recorder

Pipe has 2 desktop recorders (the HTML5 one for Chrome, Firefox, and Edge (version 79+) and the inline desktop one for Chrome on Android) but regardless of the desktop recorder used, the video quality depends hugely on 3 factors:

  1. the audio/video quality profile you chose for your embed code (influences resolution to a high degree)
  2. the quality of the hardware/camera (affects max resolution, frame rate, picture quality and noise)
  3. the light available in the room (low light results in lower fps and higher noise).

To control the resolution, Pipe offers 5 predefined audio/video quality profiles for desktop recorders:

  1. 240p : 320x240px
  2. 360p : 640x360px (default)
  3. 480p : 640x480px
  4. 720p : 1280x720px (HD)
  5. 1080p : 1920x1080px (FullHD)

The profile you choose for your desktop recorder has a very high influence on the resolution of the videos recorded with that recorder. Not all cameras support all resolutions so some cameras will return a stream in a different resolution than the one desired. The resolutions we've selected for the profiles above are very safe though.

To use one of the predefined quality profiles (240p, 360p, 480p, 720p and 1080p) just select it when generating the embed code in the Pipe account dashboard:

Setting the quality of the recording in the options menu

The 720p (HD) and 1080p (Full HD) video quality profiles are only available with trial and PRO accounts.

If you'd like to record with a certain resolution (like for example if you want to record square videos or at higher resolutions like 4K) you can create and use your own quality profiles. See using your own quality profiles for more information.

The recording resolution is not affected by the recorder size on the HTML page. You can have a small recorder embedded on your webpage in a sidebar that records videos at 1280x720 resolution.

HTML5 desktop recorder

The HTML5 desktop recorder will request the resolution mentioned in the quality profile. If the camera does not support it, the camera will return a stream with the closest possible resolution.

A framerate of 30fps is requested from the camera but it depends on the camera if can supply it. Older webcams in low-light situations will not be able to sustain this framerate, especially with high resolutions.

Firefox compresses video data using VP8 while Chrome and Edge 79+ use H.264. Pipe will convert the VP8 video data from Firefox to H.264 for the final .mp4 file that gets delivered to you.

Audio will be recorded as mono or stereo Opus audio at 48 kHz. It will be converted to AAC by Pipe for the final .mp4 file. Stereo audio will only be recorded from capable devices (Logitech C920, Logitech C925e, MacBook PRO with 3 mics, etc) on newer browsers (Chrome 63+ and Firefox 55+) and Edge 79+.

Firefox will apply an auto gain filter (tested with Firefox 68). Chrome will not apply any filters (tested with Chrome 76).

A noise suppression filter is turned on by default by Pipe on Chrome, Firefox, and Edge (tested on Edge 79). If you'd like to turn it off you can use the ns parameter in the embed code as explained in working directly with the embed code.

Inline HTML5 desktop recorder on Chrome on Android

Chrome on Android supports only these resolutions:

  • 160x120 (4:3)
  • 176x144 (4:3)
  • 320x240 (4:3)
  • 352x288 (4:3)
  • 640x360 (16:9)
  • 640x480 (4:3)
  • 1280x720 (16:9)
  • 1920x1080 (16:9)

So when using custom resolutions with the HTML5 desktop recorder inline in Chrome on Android devices keep in mind there is a high chance resolution requested may not be the resolution that is obtained. A resolution request of 400x300 will actually result in a 640x480 video stream.

Chrome on Android gives us VP8 video and Opus audio in .webm files. The data is converted to H.264 video and AAC audio in .mp4 files on our platform.

Mobile native recorder

The audio and video quality of recordings sent through our native recorder for mobile browsers depends a lot on the OS. iOS will compress the file leading to fast upload times while Android will leave the recording unchanged resulting in high-quality videos.

iOS

On iOS, a user can upload videos in 2 resolutions depending on how that video is recorded:

  • If, when prompted, the user records a new video, that video will be recorded at 360x480 (portrait) and 480x360 (landscape) at 30 FPS
  • If, when prompted, the user chooses an existing video, that video will be compressed by iOS in terms of bitrate (the resolution and FPS will remain the same)

iOS videos will have AAC audio at 48kHz and H.264 video. The data is in a .mov container.

You can read more about the audio and video quality of recordings made with iOS from this article we've written on the topic.

Android

Android devices, on the other hand, will record video at whatever resolution is set on the device and will not compress the video before uploading. This can result in long upload times but high-quality videos.

The audio codec and sample rate will depend on the device itself. We've mostly seen AAC audio at 48kHz but occasionally we do get AMR audio in 3GP containers and other exotic audio codecs and containers.

Non-AAC audio will be converted by Pipe to AAC for the final .mp4 file.

Some notes

We're seeing videos in various formats from mobile devices: mp4, mov, 3gp, webm, etc. . All these recordings are converted by Pipe to a universally working .mp4 file.

High-framerate videos (60 FPS) from mobile devices will keep their FPS value after being processed by Pipe.

When uploading slow-motion videos (120 FPS videos played back at 30 FPS), in some cases (iOS, Lenovo Vibe P2 with Android 7.0) the OS compresses the video down to 30 FPS while keeping the slow motion effect, in others (Huawei P20 Lite with Android 8.0) the original FPS value (120) is untouched.

Using your own quality profile


Pipe allows you to use your own audio/video quality profile in the desktop recorder by linking directly to it, in the embed code, using the qualityurl parameter. This ability comes in handy if, for example, you want to record square videos or higher resolution videos - like 4K - with the desktop recorder.

Step 1: create your own quality profile file and host it

Here are the 5 quality profiles available through the Pipe account dashboard: 240p, 360p, 480p, 720p, and 1080p. Download one, make your changes, and upload it to your website.

Here's every setting you can change in the profile and their default values:

Setting HTML5 HTML5 inline
video width and height ✓ ✓
optional: framerate* ✓ 30fps by default ✓ 30fps by default

* If the framerate value or tag is invalid or missing in the .xml file, no framerate constraint will be included in our request for a video stream from the webcam device. In this case, browsers might use their own default value. This is useful if you're looking to let the browser or webcam default for the requested resolution.

Step 2: link to the new .xml file in your Pipe embed

Set up the qualityurl parameter to automatically load the .xml file through http or https depending on where Pipe is embedded. Here's an example code:

var flashvars = {qualityurl: ('https:' == document.location.protocol ? 'https://' : 'http://') +"yourdomain.com/quality_profile.xml",accountHash:"your_account_hash", eid:1, showMenu:"true", mrt:5, sis:0, asv:1};

Add the pipe-qualityurl="https://yourdomain.com/quality_profile.xml" attribute to the piperecorder tag.

Add the qualityurl property to your custom JavaScript object like this qualityurl: ('https:' == document.location.protocol ? 'https://' : 'http://') +"yourdomain.com/quality_profile.xml". Your custom object will be passed as a parameter to PipeSDK.insert().

Step 3: allow Pipe to load the .xml file from your website

In case you are hosting the custom .xml audio-video quality profile file on a different domain than the one you are you are embedding the Pipe recording client in, you must enable CORS on the server on which you are hosting the custom .xml audio-video quality profile.

For example, on Apache, to add the CORS authorization header, you must add the following line in either the Directory, Location, Files or VirtualHost sections of your server config (usually located in a *.conf file, such as httpd.conf or apache.conf), or within a .htaccess file:

Header set Access-Control-Allow-Origin "https://SITE-WHERE-PIPE-IS-EMBEDDED.com"

For more details on how to configure CORS on other servers see https://enable-cors.org/server.html.

CSS classes in the v2.0 embed code


Our newer 2.0 embed code uses this CSS file to control the look and feel of the HTML5 desktop recorder and mobile native recorder.

We use classes instead of ids to target HTML UI elements because the 2.0 embed code allows for multiple recorders on the same page. All the recorders on a page will have these CSS classes applied to them.

These are the most important CSS classes:

Desktop recorder classes
Name Description
pipeRecordRTC is the main container for the elements in the initial screen
pipeStartUploading is a utility class that hides the default UI for the HTML file upload. We recommend not overriding this class
pipeCustomUpload controls the look of the Upload Video / Upload Audio button together with the class pipe-upload-label
pipeUploadAnother controls the look of the elements on the [Recorder or upload another one] screen
pipeBtn controls how the menu buttons (Record, Play, Pause, Save) look when enabled in the recording screen
pipeBtnOff controls how the menu buttons (Record, Play, Pause, Save) look when disabled in the recording screen
pipeTimer controls the look of the Pipe timer when recording and in the playback screen
pipeMicContainer
pipeMicIconNoMenu
pipeMeter-container
pipeMeter
these classes control the look and functionality of the microphone icon and microphone sound level displayed in the bottom right corner in the recording screen
pipeNormal applies to the video container on the recording screen
pipeMirrored used to mirror the video screen, we recommend not overriding this class
pipeSmallNormal controls how the small picture-in-picture video looks when playing back a video recording. We recommend not overriding this class
pipeSmallMirrored controls how the small picture-in-picture video looks when playing back a video recording, but it also mirrors it. We recommend not overriding this class
pipeMsgOverlay controls the look of the messages that are overlayed on the video
pipeError controls the look of the error screen

Mobile native recorder classes
Name Description
pipeMobileRecorder is the container for the mobile client UI elements
pipeFileInput controls the look of the custom file upload button for the mobile client

How to override the CSS classes

To override the classes you can simply add !important to your own implementation of the CSS class, for example, let's take the following Pipe CSS class:

.pipeRecordRTC span{
    position:absolute;
    margin: 0;
    left:50%;
    top:50%;
    margin-right: -50%;
    transform: translate(-50%, -50%);
    font-family:sans-serif;
    font-size:18px;
    cursor: pointer;
}

Let's say you wish to make the text in the initial recording screen on the desktop recorder a little bit smaller, just 15px. To do this you simply re-declare the CSS class in your custom CSS file that you already probably have and just add the font-size:12px !important; property:

.pipeRecordRTC span{
    font-size:15px !important;
}

That's it. Now the text in the initial recording screen on the desktop recorder will have a smaller font size.

Working directly with the embed code


You can change most of the recorder client's options directly in your embed code. The size, the maximum recording time, the payload data and more can all be changed by making changes to the embed code.

Changing the size

To change the size of the recorder client on the desktop just change the width and height values in the size object: var size = {width:320, height:270};. The size object is mandatory with the 1.0 embed code.

Changing any other option

To change any other option you need to edit or add the corresponding property of the flashvars object. This is the list of different properties that you can add or change and what they do:

Options that affect both the desktop recorder and the native mobile recording client
Name Description
accountHash (mandatory) The account hash value for your account, you will find it under https://dashboard.addpipe.com/account. This value should not be changed.
eid The ID of the environment you want the new recordings to be recorded against. It used to be a numeric value (1 for the initial Production environment and the default value) before we switched to alphanumeric environment IDs. It can be changed to any ID of an environment on your account. You can get the environment ID from the Edit environment page.
ao When set to 1, the Pipe recording client will try to record only audio. The default value is 0.
recorderId A custom string that gets sent back with every function in the desktop recorder JS Events API for the 1.0 embed code. Default value: empty string.
Options that affect only the desktop recorder
Name Description
qualityurl (mandatory) The path to the audio & video quality profile. Right now, you can choose between avq/240p.xml, avq/360p.xml, avq/480p.xml, avq/720p.xml and avq/1080p.xml or it can be the path to your custom quality profile.
mrt The maximum recording time in seconds for the desktop recording client. The default value is 600.
showMenu Whether or not the bottom control menu of the desktop recorder will be visible. The default value is true (visible). Change it to false to hide the bottom control menu of the desktop recording client.
asv Whether or not recordings streamed through the desktop recording client should be saved & processed without user interaction. When the property/attribute is missing or it is set to 1, the recordings will be saved automatically for further processing. When set to 0 the user will have to interact with the recorder's save button (or a custom button that triggers the save action through the JS APIs) for the new recording to be saved and processed. Default value: 1. We delete unsaved recording files from our media servers after one hour (starting with the moment the user has disconnected or recorded a subsequent recording in the same recorder). We've created this buffer to allow the user to reconnect - in case of disconnection - and continue recording or save an unsaved recording and, less importantly, to allow for extended playback over HTTPS.
mv Short for mirror video. When set to 1, the desktop recording client will flip the video image horizontally during recording. When it is missing or set to 0 the image during recording will not be flipped. The default value is 0. During playback, the image will not be flipped. The image in the final recording will not be flipped.
sis Short for skip initial screen. When set to 1, the initial screen with the [Record Video] or [Record Audio] text will not be shown and the desktop recording client will jump directly to the webcam recording screen. The default value is 0.
ssb Is short for show settings buttons and goes hand in hand with the showMenu setting. When set to 0 it will hide the microphone icon and microphone level meter when the bottom recorder menu is not shown. It is used in kiosk applications. Default value: 1.
avrec When set to 0, the Pipe recorder will hide the option to record video/audio from the camera. If the setting is not specified in the embed code, it will be considered to have the value 1, and the option to record video/audio will be displayed by default. Default value: 1.
dup When set to 1, the Pipe recorder will allow users to upload pre-recorded videos or audio files. Default value: 0.
srec When set to 1, the Pipe recorder will show the user the option to record the screen along with the options to record from the camera & upload an existing recording (if the options are enabled). Default value: 0.
ns When set to 0, the Pipe recorder will instruct the browser to disable any browser-based noise suppression when recording with the HTML5 desktop recorder. This might not remove any OS level or audio driver level noise suppression. Default value: 1. Desktop HTML5 recorder only.
bgCol The background color of the recorder. Must be a hexadecimal value prefixed with 0x as in 0xffffff.
cornerradius Controls the corner radius of the Pipe recorder.
menuCol The color of the control menu of the recorder. Must be a hexadecimal value prefixed with 0x as in 0xffffff.
normalCol The default color for the buttons and other clickable Pipe recorder UI elements. Must be a hexadecimal value prefixed with 0x as in 0xffffff.
overCol The hover color for the buttons and other clickable Pipe recorder UI elements. Must be a hexadecimal value prefixed with 0x as in 0xffffff.
Options that affect only the native mobile recording client
Name Description
dpv When set to 1, the native mobile recording client will attempt to hide the OS options that allow the user to select an existing recording from the photo/video library or other services like iCloud Drive on iOS, Dropbox, or Files on Android. The default value is 0. Mobile native recorder client only.

With the 2.0 HTML embed code, you use the <piperecorder> HTML attributes to control the recording client.

All the attributes are prefixed with the pipe- prefix, otherwise, they have the same names and control the same features as with the v1 embed code. Most use the same values as v1.

This is the list of attributes you can edit or add to the <piperecorder> HTML tag:

Attributes that affect both the desktop recorder and the native mobile recording client
Name Description
pipe-accounthash (mandatory) The account hash value for your account, you will find it under https://dashboard.addpipe.com/account. This value should not be changed.
pipe-eid The ID of the environment you want the new recordings to be recorded against. It used to be a numeric value (1 for the initial Production environment and the default value) before we switched to alphanumeric environment IDs. It can be changed to any ID of an environment on your account. You can get the environment ID from the Edit environment page.
pipe-ao When set to 1, the Pipe recording client will try to record audio only. The default value is 0.
Attributes that affect only the desktop recorder
Name Description
pipe-qualityurl (mandatory) The path to the audio & video quality profile. Right now, you can choose between avq/240p.xml, avq/360p.xml, avq/480p.xml, avq/720p.xml, and avq/1080p.xml or it can be the path to your custom quality profile.
pipe-width (mandatory) the width of the recorder with the desired unit
pipe-height (mandatory) the height of the recorder in pixels
pipe-mrt The maximum recording time in seconds for the desktop recording client. The default value is 600.
pipe-showmenu Whether or not the bottom control menu of the desktop recorder will be visible. The default value is 1 (visible). Change the value to 0 to hide the bottom control menu.
pipe-asv Whether or not recordings streamed through the desktop recording client should be saved & processed without user interaction. When the property/attribute is missing or it is set to 1, the recordings will be saved automatically for further processing. When set to 0 the user will have to interact with the recorder's save button (or a custom button that triggers the save action through the JS APIs) for the new recording to be saved and processed. Default value: 1. We delete unsaved recording files from our media servers after one hour (starting with the moment the user has disconnected or recorded a subsequent recording in the same recorder). We've created this buffer to allow the user to reconnect - in case of disconnection - and continue recording or save an unsaved recording and, less importantly, to allow for extended playback over HTTPS.
pipe-mv Short for mirror video. When set to 1, the desktop recording client will flip the video image horizontally during recording. When it is missing or set to 0 the image during recording will not be flipped. The default value is 0. During playback, the image will not be flipped. The image in the final recording will not be flipped.
pipe-sis Short for skip initial screen. When set to 1, the initial screen with the [Record Video] or [Record Audio] text will not be shown and the desktop recording client will jump directly to the webcam recording screen. The default value is 0.
pipe-ssb Is short for show settings buttons and goes hand in hand with the pipe-showmenu setting. When set to 0 it will hide the microphone icon and microphone level meter when the bottom recorder menu is not shown. It is used in kiosk applications. Default value: 1.
pipe-avrec When set to 0, the Pipe recorder will hide the option to record video/audio from the camera. If the setting is not specified in the embed code, it will be considered to have the value 1, and the option to record video/audio will be displayed by default. Default value: 1.
pipe-dup When set to 1, the Pipe recorder will allow users to upload pre-recorded videos or audio files. Default value: 0.
pipe-srec When set to 1, the Pipe recorder will show the user the option to record the screen along with the options to record from the camera & upload an existing recording (if the options are enabled). Default value: 0.
pipe-ns When set to 0, the Pipe recorder will instruct the browser to disable any browser-based noise suppression when recording with the HTML5 desktop recorder. This might not remove any OS level or audio driver level noise suppression. Default value: 1. Desktop HTML5 recorder only.
pipe-bgcol The background color of the recorder. Must be a hexadecimal value prefixed with 0x as in 0xffffff.
pipe-cornerradius Controls the corner radius of the Pipe recorder.
pipe-menucol The color of the control menu of the recorder. Must be a hexadecimal value prefixed with 0x as in 0xffffff.
pipe-normalcol The default color for the buttons and other clickable Pipe recorder UI elements. Must be a hexadecimal value prefixed with 0x as in 0xffffff.
pipe-overcol The hover color for the buttons and other clickable Pipe recorder UI elements. Must be a hexadecimal value prefixed with 0x as in 0xffffff.
Attributes that affect only the native mobile recording client
Name Description
pipe-dpv When set to 1, the native mobile recording client will attempt to hide the OS options that allow the user to select an existing recording from the photo/video library or other services like iCloud Drive on iOS, Dropbox, or Files on Android. The default value is 0. Mobile native recorder client only.
pipe-capture When set to user, it indicates to the browser that the user-facing camera and/or microphone should be used. When set to environment, it indicates to the browser that the outward-facing camera and/or microphone should be used. If this attribute is missing, the browser will decide what camera to use initially. If the requested facing mode isn't available, the browser may fall back to its preferred mode. Mobile native recorder client only.
pipe-capture When set to user, it indicates to the browser that the user-facing camera and/or microphone should be used. When set to environment, it indicates to the browser that the outward-facing camera and/or microphone should be used. If this attribute is missing, the browser will decide what camera to use initially. If the requested facing mode isn't available, the browser may fall back to its preferred mode. Mobile native recorder client only.

In the 2.0 JS version of the embed code, you initialize your own JavaScript object (with different properties) and pass it to the PipeSDK.insert() method

Here is a simple example: var YOUR_CUSTOM_OBJECT_NAME = { size: {width:640,height:390}, qualityurl: "avq/720p.xml", accountHash:"your_account_hash", payload:'{"userId":"55a95eeb936dd30100e0aff6","jobId":"55a7e6555f1bdc010014d6a1"}', eid:1, showMenu:1, mrt:600, sis:0, asv:0, mv:1, st:1, ssb:1, dup:1 };

And then pass it to the function: PipeSDK.insert('ID_OF_THE_DIV_TO_BE_REPLACED', YOUR_CUSTOM_OBJECT_NAME, CALLBACK_FUNCTION);

The callback function will return the actual recorder object:

PipeSDK.insert('my_ID', parametersObject, function(recorderObject){
    //custom code goes here
}

See JavaScript control API and JavaScript Events API section for information on how the recorderObject can be used.

Here is the list of object properties that you can edit or add to your recorder object:

Properties that affect both the desktop recorder and the native mobile recording client
Name Description
accountHash (mandatory) The account hash value for your account, you will find it under https://dashboard.addpipe.com/account. This value should not be changed.
eid The ID of the environment you want the new recordings to be recorded against. It used to be a numeric value (1 for the initial Production environment and the default value) before we switched to alphanumeric environment IDs. It can be changed to any ID of an environment on your account. You can get the environment ID from the Edit environment page.
ao When set to 1, the Pipe recorder will try to record audio only. The default value is 0.
Properties that affect only the desktop recorder
Name Description
size (mandatory) The object that contains the width and height of the recorder, as properties
qualityurl (mandatory) The path to the audio & video quality profile. Right now, you can choose between avq/240p.xml, avq/360p.xml, avq/480p.xml, avq/720p.xml and avq/1080p.xml or it can be the path to your custom quality profile.
mrt The maximum recording time in seconds for the desktop recording client. The default value is 600.
showMenu Whether or not the bottom control menu of the desktop recorder will be visible. The default value is 1 (visible). Change the value to 0 to hide the control menu.
asv Whether or not recordings streamed through the desktop recording client should be saved & processed without user interaction. When the property/attribute is missing or it is set to 1, the recordings will be saved automatically for further processing. When set to 0 the user will have to interact with the recorder's save button (or a custom button that triggers the save action through the JS APIs) for the new recording to be saved and processed. Default value: 1. We delete unsaved recording files from our media servers after one hour (starting with the moment the user has disconnected or recorded a subsequent recording in the same recorder). We've created this buffer to allow the user to reconnect - in case of disconnection - and continue recording or save an unsaved recording and, less importantly, to allow for extended playback over HTTPS.
mv Short for mirror video. When set to 1, the desktop recording client will flip the video image horizontally during recording. When it is missing or set to 0 the image during recording will not be flipped. The default value is 0. During playback, the image will not be flipped. The image in the final recording will not be flipped.
sis Short for skip initial screen. When set to 1, the initial screen with the [Record Video] or [Record Audio] text will not be shown and the desktop recording client will jump directly to the webcam recording screen. The default value is 0.
ssb Is short for show settings buttons and goes hand in hand with the showMenu setting. When set to 0 it will hide the microphone icon and microphone level meter when the bottom recorder menu is not shown. It is used in kiosk applications. Default value: 1.
avrec When set to 0, the Pipe recorder will hide the option to record video/audio from the camera. If the setting is not specified in the embed code, it will be considered to have the value 1, and the option to record video/audio will be displayed by default. Default value: 1.
dup When set to 1, the Pipe recorder will allow users to upload pre-recorded videos or audio files. Default value: 0.
srec When set to 1, the Pipe recorder will show the user the option to record the screen along with the options to record from the camera & upload an existing recording (if the options are enabled). Default value: 0.
ns When set to 0, the Pipe recorder will instruct the browser to disable any browser-based noise suppression when recording with the HTML5 desktop recorder. This might not remove any OS level or audio driver level noise suppression. Default value: 1. Desktop HTML5 recorder only.
bgCol The background color of the recorder. Must be a hexadecimal value prefixed with 0x as in 0xffffff.
cornerradius Controls the corner radius of the Pipe recorder.
menuCol The color of the control menu of the recorder. Must be a hexadecimal value prefixed with 0x as in 0xffffff.
normalCol The default color for the buttons and other clickable Pipe recorder UI elements. Must be a hexadecimal value prefixed with 0x as in 0xffffff.
overCol The hover color for the buttons and other clickable Pipe recorder UI elements. Must be a hexadecimal value prefixed with 0x as in 0xffffff.
Properties that affect only the native mobile recording client
Name Description
dpv When set to 1, the native mobile recording client will attempt to hide the OS options that allow the user to select an existing recording from the photo/video library or other services like iCloud Drive on iOS, Dropbox, or Files on Android. The default value is 0. Mobile native recorder client only.
capture When set to user, it indicates to the browser that the user-facing camera and/or microphone should be used. When set to environment, it indicates to the browser that the outward-facing camera and/or microphone should be used. If this attribute is missing, the browser will decide what camera to use initially. If the requested facing mode isn't available, the browser may fall back to its preferred mode. Mobile native recorder client only.
capture When set to user, it indicates to the browser that the user-facing camera and/or microphone should be used. When set to environment, it indicates to the browser that the outward-facing camera and/or microphone should be used. If this attribute is missing, the browser will decide what camera to use initially. If the requested facing mode isn't available, the browser may fall back to its preferred mode. Mobile native recorder client only.

Pipe in different languages


The Pipe recording client's default is English, supports 3 other languages, and can also use custom wording or a different language by using an external language file hosted by you.

The Pipe client supports the following languages: English (default), French, German, and Spanish.

On PC/Mac

The Pipe desktop recorder auto-detects the preferred language setting of the browser in which it is run (using the HTTP Accept-Language header) and will use that language if available.

On iOS/Android

On mobile devices the preferred language setting in the mobile OS is used, regardless of the recording client you are using (desktop recorder on Chrome 63+ on Android or the mobile native recorder).

Using your own language file


The Pipe desktop & mobile recording clients allow you to use your own language file (XML file hosted on your website) by linking directly to it, in the embed code, using the lang parameter.

Step 1: create your own language file and host it

Here is one of the default language files as an example: en.xml. Download it, translate the texts, and upload it to your website.

Step 2: link to the new .xml file in your Pipe embed code

Set up the lang parameter to automatically load the .xml file through http or https depending on where Pipe is embedded. Here's an example code:

var flashvars = {qualityurl:"avq/720p.xml", lang: ('https:' == document.location.protocol ? 'https://' : 'http://') +"yourdomain.com/language_file.xml", accountHash:"your_account_hash", eid:1, showMenu:"true", mrt:5, sis:0, asv:1};

Add the pipe-lang="https://yourdomain.com/language_file.xml" attribute to the piperecorder tag.

Add the lang property to your custom JavaScript object like so lang: ('https:' == document.location.protocol ? 'https://' : 'http://') +"yourdomain.com/language_file.xml". Your custom object will be passed as a parameter to PipeSDK.insert()

Step 3: allow Pipe to load the .xml file from your website

In case you are hosting the custom .xml language file on a different domain than the one you are you are embedding the Pipe recording client in, you must enable CORS on the server on which you are hosting the custom .xml language file

.

For example, on Apache, to add the CORS authorization header, you must add the following line in either the Directory, Location, Files, or VirtualHost sections of your server config (usually located in a *.conf file, such as httpd.conf or apache.conf), or within a .htaccess file:

Header set Access-Control-Allow-Origin "https://SITE-WHERE-PIPE-IS-EMBEDDED.com"

For more details on how to configure CORS on other servers see https://enable-cors.org/server.html

HTML5 and legacy Flash clients compared


In the 2nd half of 2016, we started working on our new HTML5 recording client and infrastructure. The 1st beta was released in December 2016 and it relied on WebRTC. Our 2nd generation non-WebRTC video recording client was released in September 2017 and it relied on the Media Recorder API (not on WebRTC). The table below documents the most important differences between the new 2nd generation HTML5-based solution (non-WebRTC) and the initial Flash-based solution.

HTML5 legacy Flash
Video and picture quality Uses the resolution specified in the loaded quality profile. The maximum possible picture quality of the webcam will be used and the bitrate will directly depend on this. Since the recorded data can be higher than the available bandwidth an upload screen might appear after the recording is stopped. Uses both the resolution and picture quality specified in the loaded quality profile. Since the recorded data can be higher than the available bandwidth an upload screen might appear after the recording is stopped.
Buffering mechanism Buffers the audio & video data locally allowing for high-quality recordings over slow connections. Buffers the audio & video data locally allowing for high-quality recordings over slow connections.
Initial video codecs VP8, VP9, or H.264 H.264
Initial audio codecs Opus @ 48kHz Nellymoser ASAO @ 44.1kHz
Requirements See Requirements and supported browsers. See Requirements and supported browsers.
Permissions needed in the browser Chrome, Firefox, or Edge camera and microphone access. Flash Player's camera and microphone access + Chrome or Firefox camera and microphone access.
Connection process The client directly connects through WSS to the media server over port 443/TCP. The client directly connects through RTMPS to the media server over port 443/TCP.

Troubleshooting connection issues


Our desktop recorder works by streaming data in real-time over WSS (secure web sockets) to a media server hosted by us.

The connection process in the HTML5 desktop recording client

The HTML5 desktop recording client connects to our ingestion media servers using WSS (secured WebSockets) over port 443.

The connection is established as soon as the user presses [Record Video], [Record Audio], or [Record Screen] on the initial screen.

Reconnection mechanism

In case of a network problem resulting in disconnection, the HTML5 desktop recorder will try to reconnect 30 times before giving up.

Each of the 30 attempts to reconnect can timeout after 10 seconds. After each timeout or failed re-connection attempt, the recorder will wait between 0.5 and 5 seconds before making another attempt. Thus, it can take a maximum of 450 seconds ((10s timeout + 5s delay) * 30 attempts = 450 seconds = 7.5 minutes) for the recorder to spend all 30 attempts. This interval can be even higher if there's anything blocking the JavaScript client side.

If a disconnection happens while recording, the recording process will continue, but a message will be shown to the user. If the connection is re-established, the data recorded while the connection was interrupted will start uploading to the media server while the recording process continues.

Connection errors

In case of a connection problem, the HTML5 desktop recorder will show one of the following messages:

Not Connected. Reconnecting in 1s...

The disconnection happens AFTER a successful wss connection has been established.

Causes:

  1. A busy or high latency WiFi network might cause continuously open wss connections to disconnect
  2. The Internet connection might fail (ISP problems, power failure, tripping over the router, etc.)
  3. Conditions on the network (proxy, firewall) have changed not allowing WebSockets connections anymore
Disconected recorder which will reconnect in 1 second due to busy network
Not Connected. Reconnecting...

Causes:

After a disconnection, Pipe will make a total of 30 attempts to reconnect to the media server. While the attempts are made, this message is shown. This message is usually shown for a fraction of a second.Disconected recorder which is trying to reconnect

Not Connected (wss). Reconnecting in 1s..

After a disconnection, when the attempt to reconnect is made through WebSockets. This message is shown if that WebSocket reconnection attempt fails. This can happen if any of the issues that caused the initial disconnect are still present.Disconected recorder which received an error when trying to reconnect

Reconnected

Shown for a moment after a successful reconnect attempt.Successful reconnected recorder

Your connection to the server has been interrupted

If disconnected, after 30 unsuccessful attempts to reconnect, the connection is considered fully interrupted.

Causes:

  1. The user is disconnected from the Internet (ISP problems, power failure, tripping over the router, etc.)
  2. The media server might be down or have no Internet connection

Recovering unsaved recordings

For recordings made with our desktop HTML5 recorder by capturing from the camera or screen, the ingestion media servers have a recording recovery mechanism in place that recovers the recording if, while streaming the recording (so while recording or uploading it), the connection is interrupted, and a new connection can't be established. This mechanism is triggered only if the autosave option is enabled in the embed code (by default, it is enabled).

HTML5 desktop recorder

Recordings are made by capturing from the camera or screen and streaming back to our ingestion servers

In case of a network problem resulting in disconnection, the HTML5 desktop recorder will try to reconnect 30 times before giving up. As shown above, this process can take up to 450 seconds and even more. That's why the ingestion media server, under normal circumstances, will wait 15 minutes - from the moment the disconnection is detected server-side - before attempting to recover an unsaved recording. It needs to give the user all the time it needs to cycle through his 30 reconnect attempts.

Uploading an existing recording

The recovery mechanism does not function with existing recordings uploaded through the desktop recording client.

Native mobile recording client

The recovery mechanism does not function with new or existing recordings uploaded through the mobile recording client.

Using webhooks instead of the JS Events API

We recommend relying on webhooks for integrating the Pipe recording client instead of relying on the JavaScript APIs because webhooks will trigger for recovered recordings. When the disconnection is the result of the user navigating away, closing the tab, browser crash, or the window/tab is accidentally closed, the JS API events will not trigger. As a result, any POST or GET requests (made within your JS) back to your server (to save the data) will never trigger. When the disconnection results from a network connection problem like a high latency wireless connection, the JS will execute on the client, but any POST or GET requests might fail.

Screen recording in HTML5 desktop recorder


Pipe can record the screen on Chrome 72+, Firefox 66+, and Edge 79+ when using the HTML5 desktop recorder.

How to enable it

From the UI

You can easily enable screen recording from the Embed section of your dashboard:

  1. Sign in to your Pipe account
  2. Go to the Embed Recorder page
  3. On the left side, in the Options panel, select the Screen recorder type
  4. Click Generate Embed Codes
  5. Copy and paste the newly generated code into your website

Enable screen reocrding in the options menu

From code


You need to add the new parameter srec and set it to 1 in your existing embed code like so: Code to enable screen recording in JavaScript


Add the pipe-srec="1" attribute to the piperecorder tag


Add the srec property to your custom JavaScript object and give it a value of 1: srec:1. Your custom object will be passed as a parameter to PipeSDK.insert()

When enabled the desktop recorder will show the [Record Screen] button:
Screen recording enable on the Pipe Recorder

When clicking [Record Screen], depending on the browser you are using, you will be able to record the following:

  • On Chrome you can record the entire screen, an application window, or a Chrome tab.
  • On Firefox you can record the entire screen or an application window.
  • On Edge you can record the entire screen, an application window, or an Edge tab.

Known issues

  • On Chrome recording a tab will throw the error Device error: no audio or video data if no activity is registered in the captured tab (no mouse movements, no scrolling etc).
  • On Firefox recording certain application windows, i.e. a Chrome window, will result in black-screen videos. The audio will be recorded as expected.

Record audio only


How to enable it

From the UI

You can easily enable Pipe to record audio only:

  1. Sign in to your Pipe account
  2. Go to the Embed Recorder page
  3. On the left in the Options panel check Record audio only
  4. Click Generate Embed Codes
  5. Copy and paste the newly generated code into your website

Setting to record audio only in the options menu

From code


Alternatively, you can also add the new parameter ao and set it to 1 in your existing embed code like so: Code to set the audio only recording in JavaScript


Add the pipe-ao="1" attribute to the piperecorder tag


Add the ao property to your custom JavaScript object and give it the value of 1: ao:1. Your custom object will be passed as a parameter to PipeSDK.insert()

How it looks

Once audio-only recording is enabled the recorder will look as follows:

Desktop recorder

Pipe Recorder with record audio only on desktop
Pipe Recorder which is recording only audio on desktop

Mobile native recorder

Pipe Recorder with record audio only on mobile

Support

On Desktop

The HTML 5 desktop recorder supports audio-only recording.

On Mobile Android Devices

Recording just audio from Android devices is supported with both the mobile native recorder and the desktop recorder.

On Mobile iOS Devices

It is not possible to record just audio from iOS devices at this time because Safari on iOS does not support audio capture through the HTML Media Capture standard. When asked to record only audio it will give the user the (wrong) option to record a video or take a picture. The latest we've tested is iOS 12.3.1.

By looking at our video recording data for September 2017 we determined only 0.4% of all recordings were recorded from iOS, the rest were recorded from Android (9.6%) and desktop devices (90%).

September 2017 recordings on different operating sistems chart

Audio Recording Quality

Desktop recorder

The HTML5 client will record audio using Opus codec at 48 kHz in a .webm container. We convert it to AAC at 48 kHz in a .mp4 file.

Mobile native recorder

When recording audio using the mobile native recorder, a wide range of containers and audio codecs will be used depending on the device and Android version. As of now the following codecs and containers are supported.

Container Audio Codec
mp3 mp3
m4a aac
3gp amr-nb
3gpp amr-nb
3g2 amr-nb
aac aac
wav -
ogg Vorbis, Opus, etc.
wma wma
flac flac
amr amr

All of them are converted to .mp4 files with AAC while keeping the original sampling frequency.

Whitelisted hosts


You can now specify a list of hosts allowed to embed the Pipe recorder. Such a list helps prevent abuse from malicious users that could embed your Pipe Recording Client embed code on other websites.

There's a different list for each of your environments.

To add to the list or edit the list go to the edit environment section and scroll down to the whitelisted hosts section. image showing a list of hosts that have been whitelisted

The list is empty by default, meaning any host is allowed.

When adding a host, do not include the protocol (https:// or http://) or any specific web page path. For example, mywebsite.com is correct, but https://mywebsite.com/signup is incorrect.

The port number does not need to be specified. If it is, it will be ignored.

You can also use localhost or any IP as a host.

You can use the wildcard prefix * to allow all subdomains.

The maximum length of any host entry must be 100 characters.

Here are some examples of how validation works:

Host entry Valid Invalid
example.com example.com www.example.com
www.example.com www.example.com example.com
*.example.com all example.com subdomains example.com
*.subdomain.example.com all subdomains of subdomain.example.com test.another-subdomain.example.com
*.*.example.com all 2-level subdomains of example.com www.example.com

When the Pipe Recording Client is embedded on a host that's not in the list, the recording client will prevent the embed code from running and show an error message.

What happens when the host information is missing ?

There are some cases where the host information (where the Pipe recorder is embedded) is not available to us. Here are two such cases:

  1. If the Pipe recorder is embedded in a page with the header Referrer-Policy set to no-referrer for non-cors request modes.
  2. If the Pipe recorder is included in a web page through an iframe with a sandbox attribute that's either empty (all restrictions are applied)) or doesn't contain the value allow-same-origin.

In such cases, if you have any entry in the list of allowed hosts above, the Pipe recorder will not run. It will show a specific error message and prevent any type of recording submission (record or upload).

Transcoding settings


Transcoding can help you in many ways. Here are a few useful use cases:

Downscaling videos

It may be the case that the majority of your user base will record/upload videos using Android which is known for producing high-resolution videos and not compressing them as iOS does. In this case, most of the videos will have at least a full HD resolution (1920x1080).

These high-resolution videos will have a large size on disk which can cause a variety of issues (slower processing, larger storage being needed, etc.). Setting the proper transcoding options can help you downscale the videos, making them smaller in size so that you can avoid those issues.

You can easily do this by following the steps:

  1. Go to the transcoding section
  2. Select Enable MP4 output
  3. For the resolution choose Scale down if needed to a maximum resolution and input the desired height, 720 for example.
  4. Save the settings.

In this case, the setup will make sure that any video with a higher height than 720px will be downscaled to 720px. The aspect ratio will be left unchanged unless set otherwise. Videos with a lower resolution than the one that was set, won't be downscaled.

Fitting videos into a fixed-size player

You may need your videos custom-fitted for a certain type of display/player.

This can easily be achieved with a few settings:

  1. Go to your transcoding section.
  2. Make sure you have Enable MP4 output selected.
  3. Choose the aspect ratio that fits your use case and select to obtain that aspect ratio either by cropping the video or by adding black bars to it.
  4. Finally you can select I want every video scaled (up or down) to a fixed resolution and set a custom height to make sure the videos will always have the same resolution.
  5. Save the settings.

This kind of setup will always produce videos of constant aspect ratio and resolution.

Pipe's complimentary storage


By default, once it's done processing a recording, Pipe stores the resulting .mp4 recording and all other corresponding files (the raw file, snapshot, and filmstrip) on our complimentary storage. We push the recording files to storage provided by Scaleway (EU2) and Amazon Web Services (EU1, US1, and US2).

Depending on the region setting for the environment you're recording with, the final recording files will be pushed to one of 4 locations:

  • EU2 (Scaleway, Amsterdam, eu2-addpipe.s3.nl-ams.scw.cloud)
  • US1 (AWS, N. California, us1-addpipe.s3-us-west-1.amazonaws.com)
  • US2 (AWS, N. Virginia, us2-addpipe.s3.us-east-1.amazonaws.com)
  • EU1* (AWS, Frankfurt, eu1-addpipe.s3.eu-central-1.amazonaws.com)

The files hosted by us on Amazon S3 (EU1, US1, US2) are encrypted at rest using Amazon S3-Managed Encryption Keys. The files hosted by us on Scaleway (EU2) are not encrypted at rest.

Object URLs

Direct private links (URLs) towards the files hosted by us are available through:

  • the Pipe account dashboard's recordings section (individual links)
  • the REST API v1
  • the video_copied_pipe_s3 webhook
  • you can produce the URLs in the browser using the data passed through the JS Events APIs functions, which give you both the final storage location and the filename or streamname
  • the snapshot and a direct (playback) link to the main file (processed mp4 or raw recording) is included in the email notification sent with each new recording
  • the export feature's "recording output data" fields include the full URL to the main file (processed mp4 or raw recording)
CDN URLs

We are experimenting with delivering the files through the Amazon CloudFront CDN for the files hosted on Amazon S3 buckets. CDN (edge) links for each file stored by us with Amazon S3 are now available from the recordings section in your Pipe account dashboard. The EU2 region uses long-term storage from Scaleway, so it does not benefit from CloudFront's CDN URLs. Only origin links will be available for these files.

Private URLs

Our storage is configured to make the files accessible through private URLs without requiring authentication. Both the CDN and the direct/original URLs are private. The URLs are private for as long as you keep them private. If you share them publicly, they will become public. This is similar to how sharing docs from Google Docs works. If you save the URLs with GravityFroms, for example, they'll be in the local GF database table.

If you need better control over where and how the files are stored, for how long, and who has access to them, you can turn off our storage (read below) and use only your storage, including Amazon S3, other S3-compatible services, Dropbox, or your server through FTP(S) and SFTP.

Using JavaScript to generate the origin URLs

When relying on the JavaScript Events API, you can generate the recording URLs using the templates below.

These are the URL templates for where each file will be stored (origin URLs):

  • processed mp4 recording: https://{LOCATION}/{ACCOUNT-HASH}/{NAME}.mp4
  • jpg snapshot: https://{LOCATION}/{ACCOUNT-HASH}/{NAME}.jpg
  • jpg filmstrip: https://{LOCATION}/{ACCOUNT-HASH}/{NAME}_filmstrip.jpg
  • raw recording: https://{LOCATION}/{ACCOUNT-HASH}/{NAME}_raw.mp4

The {ACCOUNT-HASH} value is listed in the account page. The {LOCATION} and {NAME} values are available through the JS Events API's onSaveOk, onDesktopVideoUploadSuccess, and onVideoUploadSuccess functions.

Amazon S3 Storage classes:

The EU1, US1, and US2 regions use Amazon S3 for storage. We use the following Amazon S3 storage classes to store the files:

  1. S3 Intelligent-Tiering for the resulting .mp4 file
  2. S3 Standard for the snapshot and filmstrip
  3. S3 Standard-IA (infrequent access) for the raw transcoding file

For more info on Amazon S3 storage classes check out the official documentation.

Scaleway (used for long-term storage on EU2) only has a STANDARD storage class available at the moment.

Not storing recordings on Pipe's complimentary storage

Pipe gives you the option to not store the resulting files (raw recording, processed recording, snapshot, and filmstrip) on Pipe's Amazon S3 and Scaleway storage. This option is available independently for each environment and you can find it in the storage section.

If you activate this option, we will not push the resulting recording files to our storage. As soon as the files have been pushed to all your configured storage options (FTP(S) or SFTP, S3, Dropbox) they will be deleted from our processing server.

The files will still be removed if no other storage option is configured.

This affects only the recordings made after the setting is turned on. Prior recordings won't be removed. To remove previous recordings you can delete them manually from the account dashboard, delete them through the REST API v1 (PRO feature) or use the lifecycle mechanism to delete them automatically after a number of days.

Files that fail to be pushed to your storage after up to 13 attempts across 75 hours since the recording was saved to our database are kept for 28 EEST/EET days to allow both parties to access the files and attempt a manual push.

In the case of recordings that fail to transcode after up to 13 attempts across 75 hours since the recording was saved to our database, we keep the original recording for 28 EEST/EET days to allow both parties to access the file and understand why the transcoding failed (invalid recording, exotic codecs, empty recording, etc.).

CORS headers

The buckets we use to store recordings have CORS enabled meaning that if you send the Origin header as part of your GET requests, the S3 bucket will reply with the actual recording together with the following headers:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET
Access-Control-Max-Age: 3000

Here's how the request/response CORS headers look in Postman:
CORS headers received when origin header is sent

These CORS headers come in handy if, for example, you want to load a video recording in the browser and apply effects to it using Seriously.js.

Pushing the recording files through FTP(S)


The Pipe Platform can push the recording files to your destination server through the File Transfer Protocol.

You can only use one of the two protocols (FTP(S) or SFTP) at a time.

To set up the FTP(S) transfer, fill in the form on the FTP(S) page with the required credentials. The resulting recording files (mp4, raw recording, snapshot, and filmstrip) of new recordings will be pushed by the Pipe Platform to your storage using FTP(S). Currently, the Pipe Platform can't be set up to push the files of existing recordings that have been processed.

When you choose FTP(S) as a storage option, you must fill in the following credentials:

  1. Host* - The IPv4 or domain name of your FTP(S) server. We do not support IPv6. This value shouldn't have any trailing slashes and shouldn't be prefixed with ftp://. The max string length for the IP or domain name is 100 characters.
  2. Port* - The port on which your FTP(S) server is listening (default port is 21). A number between 0 and 65536.
  3. Username* - The username that will be used to log in to your FTP(S) server. The max string length for the username is 100 characters and it is case-sensitive.
  4. Password* - The password that will be used to log in to your FTP(S) server. The max string length for the password is 100 characters and it is case-sensitive.
  5. Folder - The folder in which the files will be pushed (the path must exist and the user should have write privileges). The max string length for the folder name is 100 characters.

At any time, you can manually remove your FTP(S) credentials. The credentials are also deleted from our database when your account is manually or automatically deleted.

If you're using an FTPS (FTP over SSL/TLS) connection, please ensure that your FTP server is configured to support explicit FTP over SSL/TLS connections and that any firewall (cloud based or OS level) is configured to allow both the initial FTP control connection (usually on port 21) and the subsequent FTP data connection (towards the passive ports).

An FTP(S) test feature is available in the Pipe account dashboard, which will establish a connection, authenticate using the credentials in the form, change to the specified folder, upload a file (addpipe-ftp-test.test) and delete the file. As a result, the test will ensure your host is reachable, the username and password are correct, the folder exists, and the user has permission to write to the specific folder.

The production servers opening the FTP(S) connections have the following IPs:

  • 162.55.182.247 (EU2 region)
  • 167.99.110.163 (US1 region, west)
  • 68.183.96.15 (US2 region, east)
  • 116.203.232.162 (FTP(S) test)
  • 206.189.14.52 (EU1 region)

The Pipe Platform will first open a FTP(S) control connection to your server's port 21 (or to the configured port). A 2nd data transfer connection will be opened in passive mode. As a result, if your FTP(S) server is protected by a firewall, your firewall rules must allow inbound connections on port 21 and inbound connections on the ports configured in your FTP(S) server for use with passive connections.

The files pushed by the Pipe Platform to your FTP(S) server are transferred in binary mode.

There is a 10-second timeout for each FTP command (connect, put file, etc.) sent by us to your FTP(S) server.

We do not support anonymous connections.

All files belonging to a recording are pushed sequentially through the same FTP(S) connection pair. There will be a new connection pair for each such group of files.

Previous implementations of our FTP(S) push feature pushed an extra file, named addpipe-ftp-test.test, to your storage to test whether the passive data connection worked. This is not happening anymore, and the file can be safely deleted.

Whether or not a push attempt is deemed successful depends on establishing the FTP(S) connection, authenticating, switching to the deiserd folder, and pushing the main recording output file (processed .mp4 recording or raw recording) to the designated folder.

Securing your FTP setup

Plain FTP does not encrypt its traffic; all transmissions are in clear text, and usernames, passwords, commands, and data can be read by anyone able to perform packet capture (sniffing) on the network. That's why we strongly recommend you use FTPS whenever possible.

  • Use FTPS (FTP over SSL/TLS) instead of plain FTP
  • limit the IPs that can open a connection to your server's FTP port (21 is the default) to the IPv4 addresses above.
  • create a new FTP user that has writing permissions only on the folder where you want the final recording files to be pushed.
  • Set a disk quota for the user so that someone with access to the user credentials can't mistakenly or maliciously fill up your disk.
  • Disable your FTP user's ability to delete files. You can do that by denying deletion commands in your FTP server's configuration.
  • Set up a cron job or similar mechanism to periodically scan the folder and copy out files.
  • Don't expose your FTPS credentials by using them with FTP.

If, for some reason, you have to use plain FTP, the above measures can limit and isolate the damage that this insecurity brings. Adopting these measures does not in any way secure the data transmission over plain FTP.

FTP(S) Logs

The logs available at https://dashboard.addpipe.com/ftp_logs contain information about the attempts to push recording files to your storage through FTP(S).

Recording ID The ID of the recording for which this log was saved.
Date & time Date and time for when the log was saved to our database.
FTP(S) details The details of FTP(S) connection:
  • Host - The address of the server to which we attempted to open the FTP(S) connections.
  • Username - The username.
  • Folder - The destination folder.
  • Protocol/Port - The protocol and the port used to establish the connection.
File(s) The names of the files we attempted to upload. Up to 4 files will be listed here.
Status The upload status.

FTP(S) Upload Status Types

OK Upload of the main recording file (mp4 or raw recording) was successful.
FILE_MISSING The local main recording file (mp4 or raw recording) on Pipe's transcoding server is missing.
UPLOAD_FAILED This may be triggered by various causes, but mostly it is a permission problem on the remote folder, a connection interruption while uploading, a file size constraint, or the firewall blocking the passive connection's port.
FTP_LOGIN_FAILED Either the username or password for the FTP(S) connection is incorrect.
INCORRECT_FTP_FOLDER The directory or the path to the directory in which the files should have been uploaded did not exist or was incorrectly named.
FTP_PASV_FAILED Could not switch the remote FTP(S) server to passive mode for the FTP(S) data connection.
FTP_CONNECTION_TIMEOUT The attempt to open a FTP(S) connection to the remote server did not succeed in 10 seconds and timed out. This can happen if the remote server is behind a firewall, does not accept remote connections, or is offline altogether.

Pushing the recording files through SFTP


The Pipe Platform can push the recording files to your destination server through the SSH File Transfer Protocol.

You can only use one of the two protocols (FTP(S) or SFTP) at a time.

The default SSH/SFTP port is 22. If a firewall protects your destination server, your firewall rules must allow inbound connections on port 22 from the IPv4 addresses below.

We only support password-based authentication. We don't support none, public-key, host-based, or keyboard-interactive authentication.

All files belonging to a recording are pushed sequentially through the same SFTP connection. There will be a new connection for each such group of files.

Whether or not a push attempt is deemed successful depends on establishing the SFTP connection, authenticating, and pushing the main recording output file (processed .mp4 recording or raw recording) to the designated folder.

To set up the SFTP transfer, fill in the form on the SFTP page with the required credentials. The resulting recording files (mp4, raw recording, snapshot, and filmstrip) of new recordings will be pushed by the Pipe Platform to your storage using SFTP. Currently, the Pipe Platform can't be set up to push the files of existing recordings that have been processed.

When you choose SFTP as a storage option, you must fill in the following credentials:

  1. SFTP Host* - The IPv4 or domain name of your SFTP server. We do not support IPv6. The max string length for the IP or domain name is 100 characters.
  2. Port* - The port on which your SFTP server runs SSH (default port for SSH is 22). A number between 0 and 65536.
  3. SFTP Username* - The username which will be used to login to your SFTP Server. The max string length for the username is 100 characters and it is case-sensitive.
  4. SFTP Password* - The password which will be used to login to your SFTP Server. The max string length for the password is 100 characters and it is case-sensitive.
  5. SFTP Folder - The folder in which the files will be pushed (the path must exist and the user should have write privileges). The max string length for the folder name is 100 characters.

At any time, you can manually remove your SFTP credentials. The credentials are also deleted from our database when your account is manually or automatically deleted.

The production servers opening the SFTP connection have the following IPs:

  • 162.55.182.247 (EU2 region)
  • 167.99.110.163 (US1 region, west)
  • 68.183.96.15 (US2 region, east)
  • 116.203.232.162 (SFTP test)
  • 206.189.14.52 (EU1 region)

Securing your SFTP setup

For a more secure SFTP setup, we recommend the following measures:

  • Important: limit the IPs that can open a connection to your server's SSH/SFTP port (22 is the default) to the IPv4 addresses above.
  • Restrict the user account to have access to the SFTP subsystem only. That way they can't get a shell. You can do that by adding the following line, ForceCommand internal-sftp in /etc/ssh/sshd_config.
  • Set a disk quota for the user account so that someone with access to the user credentials can't mistakenly or maliciously fill up your disk. Digital Ocean has a tutorial on How To Set Filesystem Quotas.
  • Disable your user's ability to delete files or folders. You can do that by denying remove requests with ForceCommand internal-sftp -P remove,rmdir in /etc/ssh/sshd_config.
  • Set up a cron job or similar mechanism to periodically scan the folder and copy out files.

SFTP Logs

The logs available at https://dashboard.addpipe.com/sftp_logs contain information about the attempts to push recording files to your storage through SFTP.

Recording ID The ID of the recording for which this log was saved.
Date & time Date and time for when the log was saved to our database.
SFTP details The details of SFTP connection:
  • Host - The address of the server to which we attempted to open a SFTP connection.
  • Username - The username.
  • Folder - The destination folder.
  • Protocol/Port - The protocol and the port used to establish the connection.
File(s) The names of the files we attempted to upload. Up to 4 files will be listed here.
Status The upload status.

SFTP Upload Status Types

OK Upload of the main recording file (mp4 or raw recording) was successful.
FILE_MISSING The local main recording file (mp4 or raw recording) on Pipe's transcoding server is missing.
UPLOAD_FAILED This may be triggered by various causes, but mostly it is a permission problem on the remote folder, a connection interruption while uploading, or a file size constraint.
SFTP_LOGIN_FAILED Either the username or password for the SFTP connection is incorrect.
INCORRECT_SFTP_FOLDER The directory or the path to the directory in which the files should have been uploaded did not exist or was incorrectly named.
SFTP_CONNECTION_TIMEOUT The attempt to open a SFTP connection to the remote server timed out. This can happen if the remote server is behind a firewall, does not accept remote connections, or is offline altogether.

Pushing recordings to Amazon S3 buckets or compatible services


The Pipe Platform can push the resulting files (MP4 file, original recording, snapshot, and filmstrip) to your Amazon S3 bucket or other S3-compatible services for storage and delivery.

Our push to S3 mechanism supports all standard AWS regions, custom endpoints, canned ACLs, folders, storage classes, cross AWS account pushes, multipart uploads, and URLs.

S3 bucket names

We validate bucket names used in the Pipe Platform against the official Amazon S3 bucket naming rules. Dots are allowed in bucket names. Even though your S3-compatible storage provider might have other naming rules, we must enforce the official AWS rules because we use the official AWS SDKs.

Multipart uploads

Recordings larger than 100MiB are pushed to Amazon S3 or compatible services using multipart upload.

  1. This method ensures improved throughput (faster uploads), especially when the S3 bucket is far away from our transcoding servers (when pushing from our EU2 region to an Amazon S3 bucket in Mumbai, for example).
  2. Multipart upload is the only way to push files larger than 5GiB to Amazon S3.
  3. On choppy connections, only parts that failed to upload will be retried (up to 3 times) instead of retrying the entire file. This should translate into faster uploads.

With Amazon S3, for multipart uploads to work, your IAM user needs to have the s3:AbortMultipartUpload action allowed through the attached policy.

Object URLs

When we push a file to your S3 storage, the storage service should generate a https link (named Object URL) for the file. This link will be shown in the Pipe account dashboard's s3_logs page and will be pushed to you through the video_copied_s3 webhook. With Amazon S3, this link is named Object URL and is available in the AWS Management Console for every file hosted on your S3 bucket.Amazon S3 Management Console showing the https Object URL for a file in the bucket

Whether or not the object or file is publicly accessible directly through the link is governed by various controls. With Amazon S3 you have the following:

  1. For buckets with ACL enabled: ACL permissions active on the file (initially set through the ACL setting in the Pipe account dashboard's S3 credentials section) and Block public access bucket settings regarding ACL permissions.
  2. for buckets with ACL disabled: the bucket policy and Block public access bucket settings regarding bucket policies

The blog post named Amazon S3 Object URLscontains more info on the topic.

Pushing to Amazon S3 buckets

You'll need an AWS account, an S3 bucket, and an IAM user with the correct permissions and its security credentials. We will cover these below.

In the end, you'll have to provide the following information in the Pipe account dashboard:

  • Access Key ID for the IAM user
  • Secret Access Key for the IAM user
  • Bucket name
  • Bucket region (or custom endpoint)

You'll also be able to configure the following:

  • the folder in the bucket where to put the files
  • the type of canned ACL
  • storage class

Basic setup

The four steps below will guide you through setting up the Pipe Platform, IAM user, and the S3 bucket so that the files will be publicly accessible through private links. As a result, any person (or system) with the links will be able to access, play, or download the files, straight from the Amazon S3 bucket, without authenticating. This setup will be achieved using ACLs, but you can also do it using bucket policies.

The Object Ownership and Block all public access settings at the bucket level and canned ACL option chosen in the Pipe Platform are strongly linked and govern whether or not the files will be publicly accessible through private links/URLs.

Step 1: Create an AWS account

You can sign up for an AWS account at https://aws.amazon.com/free/. There's a Free Tier that includes 5GB of standard S3 storage for 12 months. This guide from AWS covers in detail how to create a free account.

Step 2: Create an Amazon S3 bucket

With the AWS account created, you now need an S3 bucket so let's make one:

  1. While signed in to the AWS Management Console, go to the Amazon S3 section.
  2. Click the [Create Bucket] button at the top right.
  3. Give the new bucket a name (you must respect the bucket naming rules).
  4. Select a region (newer regions are not enabled by default)
  5. For Object Ownership, select ACLs enabled. The secondary option between Bucket owner preferred and Object writer is not important for this guide.
  6. Uncheck Block all public access and make sure all four suboptions are unchecked.
  7. Now click [Create Bucket] at the bottom.
Step 3: Create an IAM user, get the access key & secret

To create an IAM user:

  1. While signed in to the AWS Management Console, go to the Identity and Access Management section.
  2. On the left side menu, under Access Management, click on Users, and in the new section, click on [Add Users].
  3. Give the new user a name and click [Next].
  4. Under Set permissions, click on Attach existing policies directly; search for and check the AmazonS3FullAccess policy, then click on [Next].

    Seting Programmatic access and an AmazonS3FullAccess policy

  5. Under Review and create, click on [Create user].
  6. Your user will be created and will be listed on the IAM users page.
  7. Click on the user and will be taken to a new page where you will be able to create security credentials for programmatic access.
  8. Click on the [Security credentials] tab.
  9. Scroll down to the [Access keys] panel; click on [Create access key].

    AWS IAM access key create panel

  10. You will be taken to a new page Access key best practices & alternatives. Here select the option Application running outside AWS and then click [Next].
  11. You can skip the Set description tag section so click on [Create access key].
  12. Both the Access key ID and secret access key will be available on this page. Click on the show link under Secret access key to show your secret access key. Copy and paste it somewhere safe because it is the only time when the secret key can be viewed. The Access key ID, on the other hand, will be available after you leave this page. You can also download the credentials as a .csv file.

    AWS IAM access key created dialog box

Step 4: Configure the Pipe Platform to push the files to your S3 bucket through your new IAM user
  1. Go to the S3 section in your Pipe account dashboard.
  2. Fill in the Access Key ID and Secret Access Key.
  3. Type in the bucket name and select the corresponding region.
  4. Type in a folder name or leave the folder value empty to have the files put in the bucket's root. We also support subfolders.
  5. For Canned ACL, select Public-read. This choice will ensure the files will be publicly accessible through the private Object URLs generated by Amazon S3.
  6. Click [Save Amazon S3 Credentials].

At any time, you can manually remove your S3 credentials. The credentials are also deleted from our database when your account is manually or automatically deleted.

For each new recording, the Pipe Platform will now attempt to push the resulting files to your S3 bucket. What files are created and pushed depends on the outputs enabled in the Transcoding Engine section of the Pipe account dashboard.

The production servers, which will push the files to the Amazon S3 buckets, have the following IPs:

  • 162.55.182.247 (EU2 region)
  • 167.99.110.163 (US1 region, west)
  • 68.183.96.15 (US2 region, east)
  • 206.189.14.52 (EU1 region)

For a more secure setup, we recommend limiting your IAM user policy to our IPs. To do that, you can follow the steps from this tutorial.

Disabled AWS regions

Some AWS regions, like Europe (Milan), Asia Pacific (Hong Kong), and Middle East (Bahrain), are disabled by default in all AWS accounts. To enable them, follow this guide.

IAM users, policies & actions

If you've created a custom policy for your IAM user, you need to add the following actions to the policy:

  1. s3:PutObject for the IAM user to have permission to upload the recording files.
  2. If you have ACLs enabled on your bucket, some ACLs will require that the IAM user has the s3:PutObjectAcl action as well. If this action is needed but missing, you will get a 403 Forbidden response from AWS, which will be visible in the S3 logs.
  3. s3:AbortMultipartUpload is needed in relation to multipart uploads.

For a more secure setup, the actions above need to be granted on the specific S3 bucket where you want the files uploaded.

For a quicker but less secure setup, the AmazonS3FullAccess IAM policy includes all needed actions, and the * resource or All resources option includes all possible S3 buckets.

The IAM user can belong to the same AWS account as the bucket, or it can be on a different account (cross-account pushes).

Bucket settings: Object Ownership

ACLs enabled

To push the files to a bucket with Object Ownership set to ACLs enabled, the canned ACL setting in the Pipe Platform can be set to any of the seven available options.

The chosen canned ACL and the Block public access bucket level setting will impact who (bucket owner, file owner, anyone on the Internet, etc.) has what permissions on the files. For the HTTP links/URLs generated by Amazon S3 for each file to be publicly accessible, choose public-read or public-read-write (these two canned ACLs differ only when applied to buckets, and we're setting them on files pushed to your S3 bucket).

If you're doing the push cross-account (the S3 bucket belongs to account A and the IAM user belongs to account B), then the S3 bucket needs to grant the Write permission to the canonical ID of account B in the Access control list section.

You can read more about ACLs and canned ACLs in the official ACL documentation.

ACLs disabled

For us to push the files to a bucket with Object Ownership set to ACLs disabled - which is the currently default and recommended setting - the canned ACL setting in the Pipe Platform needs to be set to none or to bucket-owner-full-control. private and bucket-owner-read will also work but only when the IAM user and the S3 bucket belong to the same AWS account.

If you're doing the push cross-account (the S3 bucket belongs to account A and the IAM user belongs to account B), then the S3 bucket needs its own policy allowing the user from account B to write to it.

The bucket-level policy and Block public access settings will establish whether or not the HTTP links/URLs generated by Amazon S3 for each file will be publicly accessible.

You can read more about controlling ownership of objects and disabling ACLs for your bucket in the official Object Ownership documentation.

Canned ACLs

When using ACLs, you give up to 4 permissions (READ, WRITE, READ_ACP, WRITE_ACP) on S3 buckets and files to various grantees (the file owner, AWS groups, everyone on the Internet, other AWS accounts, etc.) in a similar way to how file permissions work on Linux.

More specifically, when using ACLs with files pushed by Pipe to your Amazon S3 storage, you give up to 3 permissions READ, READ_ACP & WRITE_ACP (WRITE is not supported by files, only by buckets) on the uploaded file to certain grantees (the file owner, the bucket owner, the AuthenticatedUsers group and the AllUsers group which means everyone on the Internet).

When Pipe pushes the recording files to your Amazon S3 bucket with ACLs enabled, it can use one of 6 canned (predefined) ACLs to set the permissions on each file. Here's our description of each canned ACL:

Private
The default ACL. The file owner (root AWS account - regardless if the file was pushed through an IAM account or a root account - set up to push the file to your S3 bucket) gets FULL_CONTROL. For files, that means READ, READ_ACP, and WRITE_ACP permissions on the file. There is no WRITE permission available for files. No one else has access rights.
Public-read
The file owner gets READ, READ_ACP, and WRITE_ACP permissions on the file, just like with the private ACL above. The AllUsers group gets READ access, meaning anyone can access the file through the URL (link) generated by AWS. Select this option if you want the file to be publicly accessible to anyone with the link.
Public-read-write
For our purposes, this ACL has the same effect as Public-read. The extra WRITE permission that the AllUsers group gets through this canned ACL has no effect on files, only on buckets.
Authenticated-read
Similar to Public-read, but it is the AuthenticatedUsers group that gets READ access. This means that an authenticated GET request against the URI (link) generated by AWS will return the file/resource. For a request to be authenticated, you need to send the Authorization header value as part of the request. Postman makes it easy to generate such requests.
Bucket-owner-read
Useful when you're using an AWS account to write files to an S3 bucket from a different AWS account. Object owner gets FULL_CONTROL. The bucket owner gets only READ access.
Bucket-owner-full-control
Useful when you're using an AWS account to write files to an S3 bucket from a different AWS account. Both the object owner and the bucket owner get FULL_CONTROL over the object (meaning READ, READ_ACP, and WRITE_ACP permissions).
None
The recommended option if the bucket has ACLs disabled. If the bucket has ACLs enabled, it will automatically default to the Private ACL.

You'll find more information on ACLs and each canned ACL in the official ACL documentation from AWS.

Amazon S3 Storage classes

Amazon S3 offers a range of storage classes designed for different use cases. You can read more about each storage class in the official Amazon S3 documentation:

Pushing to DigitalOcean Spaces

The subject of pushing the recording files to DigitalOcean Spaces has been covered in this tutorial.

Pushing to Google Cloud Storage

The subject of pushing the recording files to Google Cloud Storage has been covered in this tutorial.

S3 Logs

The logs available at https://dashboard.addpipe.com/s3_logs contain information about the attempts to push the files to your Amazon S3 bucket or compatible services.

The logs table contains the following information:

Recording ID The ID of the recording whose files were pushed to your bucket.
Date & time Date and time for when the log was saved to our database.
Details The start of the key & secret; the bucket name, folder & region; the custom endpoint if any, canned ACL, and storage class.
Status The status returned by AWS.
Exception Any exception returned by AWS.
Object URLs The links received from the S3 storage provider to the files hosted on your bucket. With AWS, every file uploaded to an S3 bucket has an Object URL and bucket policies and ACLs will govern the file's (public) accessibility via the URL.

The Status column can have one of these three values:

OK Upload of the main recording file (mp4 or raw recording) was successful.
FILE_MISSING The local main recording file (mp4 or raw recording) on Pipe's transcoding server is missing.
FAIL This may be triggered for all the recording associated files by various errors, which should be displayed in the Exception column.

Pushing recording files to Dropbox


The resulting files (MP4 files, original recording, snapshot, filmstrip) can be pushed to your own Dropbox account for storage. The Pipe - Dropbox integration works with both personal Dropbox accounts and Dropbox member accounts that are part of a Dropbox Team/Business account.

New Dropbox Integration

To push the resulting recording files to your Dropbox account, you need to connect the Pipe Recording Dropbox App to your Dropbox account.

These are the steps you need to follow:

  1. In the Pipe account dashboard > Storage > Your Storage > Dropbox section, click on the button [Connect Dropbox App].
  2. A new window will open (you may be asked first to log in to Dropbox) asking you to make sure you want to connect to the app. Click [Continue].
  3. You will be presented with a screen that shows what permissions the Pipe Recording App would like to obtain. Click [Allow]. That's it! You will automatically be redirected back to the Pipe account dashboard.

The Dropbox Folder is now optional. The recording files will be pushed to /Apps/Pipe Recording App/{folder name}.

Disconnecting Dropbox

At any time, you can disconnect the Pipe Recording App from the connected apps section within your Dropbox account settings. The credentials are also deleted from our database when your account is manually or automatically deleted.

Dropbox Team accounts

Pipe can push the recording files to your personal Dropbox account or to your Dropbox member account which is part of a bigger Dropbox Teams/Business account. Each member of a team account needs to connect the Pipe Recording App individually as shown above. At the moment, the integration can't be set up by the Dropbox team admin for one or more of the members. If you need such a Pipe-Dropbox integration let us know why by e-mailing us at contact@addpipe.com.

Dropbox Logs

The Dropbox storage logs (available at https://dashboard.addpipe.com/dropbox_logs) will show the following:

Recording ID The id of the recording.
Date & time Date and time the push attempt was made. They're in your account's time zone.
Dropbox details Part of the token and the folder.
File(s) The names (including extensions) of the files that we attempted to push.
Status The status returned by the Dropbox API (errors will be displayed here if the case).

Dropbox Upload Status Types

UPLOAD_OK The upload of the file was successful.
FILE_DOES_NOT_EXIST The local files on Pipe's processing server are missing.
FAIL This may be triggered by various Dropbox errors. They're usually related to disk space on your Dropbox account or trying to upload to an inexistent folder.
The given OAuth 2 access token doesn't exist or has expired This is exactly what the error says.
Dropbox-specific error in JSON format This is usually self-explanatory and contains a lot of details.

Old Dropbox Integration

Creating your Dropbox application

The 1st thing you need is a Dropbox app so let's create one:

  1. Sign in to your Dropbox account at dropbox.com.
  2. After signing in, go to https://www.dropbox.com/developers, click on [App Console] in the top right, and then click on [Create App]:Dropbox 'create app' button
  3. On the next page:
    • Under Choose an API select Scoped access
    • Under Choose the type of access select App folder – Access to a single folder created specifically for your app.
    • Name your app & click [Create App]

Obtaining the Dropbox access token

  1. After the app is created you will be redirected to the app settings tab. At the top, switch to the [Permissions] tab.
  2. In the Files and folders permission section check the files.content.write permission and click [Submit].
  3. Go back in the [Settings] tab, scroll down a bit, and, under the OAuth 2 section, from the Access token expiration dropdown select the [No Expiration] option:
    Setting the option to generate a token with no expiration date in Dropbox
  4. Click [Generate] to generate the access token:
    Dropbox 'generate token' button

Configuring the Pipe environment to push the resulting files to your Dropbox folder using your new token

After you get the access token, go to the Dropbox tab in your Pipe account and paste it into the form.

Input fields for Dropbox credentials on Pipe account dashboard

For the Dropbox Folder, you can choose any name you wish. The recording files will be pushed to /Apps/{your app name}/{folder name}.

After you click [Save Dropbox Credentials], new recordings (and any associated files like the snapshot or filmstrip) will be pushed to your Dropbox folder.

Dropbox Team accounts

Pipe can push the recording files to your personal Dropbox account or to your Dropbox member account which is part of a bigger Dropbox Teams/Business account. Each member of a team account needs to connect the Pipe Platform to their Dropbox account individually using their own app as shown above. At the moment, the integration can't be set up by the team admin for one or more of the members. If you need such Pipe-Dropbox integration let us know why by e-mailing us at contact@addpipe.com.

Setting up webhooks


Webhooks allow you to create integrations that subscribe to certain events in Pipe. When one of those events happens, we'll trigger a webhook that'll send (POST) the event data to the webhook's URL. Pipe allows webhook URLs with up to 200 characters in length.

Webhooks can be used to receive & save information about new recordings to your database, trigger a copy action to your own storage, or execute any other code.

Multiple webhooks can be set on each environment and each webhook can be triggered on multiple events.

Setting up your first webhook

Go to your account and click on the Webhooks tab. To add a new webhook simply click on the Add New Webhook button. You will be taken to the webhook setup page:

Setting up a webhook in Pipe account dashboard

Webhook events

There are 6 events in a recording's timeline you can subscribe to with each webhook:

  1. video_recorded: sent as soon as a new recording is finished, saved to our database, and a virtual worker has been assigned to process it (recording is not yet processed or pushed to storage).
  2. video_converted: sent right after a recording is converted to .mp4. If the mp4 output is disabled in the Transcoding Engine, this webhook will not be sent.
  3. video_copied_pipe_s3: sent right after a recording is successfully pushed to Pipe's complimentary long-term storage (Amazon S3 or Scaleway).
  4. video_copied_ftp: sent right after a recording is successfully pushed to your SFTP or FTP(S) storage.
  5. video_copied_s3: sent right after a recording is successfully pushed to your S3 storage.
  6. video_copied_dbox sent right after a recording is successfully pushed to your Dropbox account.

Data sent

Data can be sent with one of the two following Content-Type headers:

  1. application/json: the JSON data will be sent directly as the body of the POST request. This is great if you wish to integrate with services like AWS Lambda.
  2. application/x-www-form-urlencoded: a payload variable containing the JSON data about the event will be sent.

With every webhook event, a variable named payload will be sent (POST-ed) to the specified URL with the Content-Type header set to application/x-www-form-urlencoded. The value of the payload variable will be different for each event type. Here are a few examples of what you can expect:

payload={
	"version":"1.0",
	"event":"video_recorded",
	"data":{
		"videoName":"STREAM_NAME",
		"audioCodec":"NellyMoser ASAO",
		"videoCodec":"H.264",
		"type":"flv",
		"id":123,
		"dateTime":"2016-03-03 15:51:44",
		"timeZone":"Europe/Bucharest",
		"payload":"your payload data string",
		"httpReferer":"http://site_from_where_video_was_recorded.com",
		"cam_name":"Logitech HD Pro Webcam C920 (046d:082d)",
		"mic_name":"Default",
		"ip":"91.16.93.181",
		"ua":"Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Mobile Safari/537.36"
	}
}
payload={
	"version":"1.0",
	"event":"video_converted",
	"data":{
		"videoName":"STREAM_NAME",
		"duration":7,
		"audioCodec":"AAC",
		"videoCodec":"H.264",
		"type":"mp4",
		"size":194373,
		"width":"320",
		"height":"240",
		"orientation":"landscape",
		"id":123,
		"dateTime": "2015-10-10 16:00:36",
		"timeZone":"Europe/Bucharest",
		"payload":"your payload data string"
	}
}
payload={
	"version":"1.0",
	"event":"video_copied_pipe_s3",
	"data":{
	    "storedStatus":"stored successful",
		"videoName":"STREAM_NAME",
		"size":194373,
		"checksum_md5":"968302a32f7c7ed67523274aa8a92717",
		"checksum_sha1":"b733ec235ea57119172c8b044220e793446063fe",
		"id":123,
		"url":"https://addpipevideos.s3.amazonaws.com/b8e2f5bfd04a93b434bd8c740bff744d/STREAM_NAME.mp4",
		"rawRecordingUrl":"https://addpipevideos.s3.amazonaws.com/b8e2f5bfd04a93b434bd8c740bff744d/STREAM_NAME_raw.EXTENSION",
		"snapshotUrl":"https://addpipevideos.s3.amazonaws.com/b8e2f5bfd04a93b434bd8c740bff744d/STREAM_NAME.jpg",
		"filmstripUrl":"https://addpipevideos.s3.amazonaws.com/b8e2f5bfd04a93b434bd8c740bff744d/STREAM_NAME_filmstrip.jpg",
		"cdn":{"cdnRecordingUrl":"https://recordings-eu.addpipe.com/b8e2f5bfd04a93b434bd8c740bff744d/STREAM_NAME.mp4",
			"cdnRawRecordingUrl":"https://recordings-eu.addpipe.com/b8e2f5bfd04a93b434bd8c740bff744d/STREAM_NAME_raw.EXTENSION",
			"cdnSnapshotUrl":"https://recordings-eu.addpipe.com/b8e2f5bfd04a93b434bd8c740bff744d/STREAM_NAME.jpg",
			"cdnFilmstripUrl":"https://recordings-eu.addpipe.com/b8e2f5bfd04a93b434bd8c740bff744d/STREAM_NAME_filmstrip.jpg"},
		"bucket":"eu1-addpipe",
		"region":"eu-central-1",
		"payload":"your payload data string",
	}
}
payload={
	"version":"1.0",
	"event":"video_copied_ftp",
	"data":{
		"ftpUploadStatus":"upload success",
		"protocol":"ftp",
		"videoName":"STREAM_NAME",
		"type":"mp4",
		"size":493534,
		"checksum_md5":"968302a32f7c7ed67523274aa8a92717",
		"checksum_sha1":"b733ec235ea57119172c8b044220e793446063fe",
		"id":123,
		"payload":"your payload data string"
	}
}
payload={
	"version":"1.0",
	"event":"video_copied_s3",
	"data":{
		"s3UploadStatus":"upload success",
		"videoName":"STREAM_NAME",
		"type":"mp4",
		"size":493534,
		"checksum_md5":"968302a32f7c7ed67523274aa8a92717",
		"checksum_sha1":"b733ec235ea57119172c8b044220e793446063fe",
		"id":123,
		"url":"https://bucketname.s3.amazonaws.com/b8e2f5bfd04a93b434bd8c740bff744d/STREAM_NAME.mp4",
		"rawRecordingUrl":"https://bucketname.s3.amazonaws.com/b8e2f5bfd04a93b434bd8c740bff744d/STREAM_NAME_raw.EXTENSION",
		"snapshotUrl":"https://bucketname.s3.amazonaws.com/b8e2f5bfd04a93b434bd8c740bff744d/STREAM_NAME.jpg",
		"filmstripUrl":"https://bucketname.s3.amazonaws.com/b8e2f5bfd04a93b434bd8c740bff744d/STREAM_NAME_filmstrip.jpg",
		"bucket":"bucketname",
		"region":"us-east-1",
		"acl":"public-read",
		"payload":"your payload data string"
	}
}
payload={
	"version":"1.0",
	"event":"video_copied_dbox",
	"data":{
		"dboxUploadStatus":"upload success",
		"videoName":"STREAM_NAME",
		"type":"mp4",
		"size":493534,
		"checksum_md5":"968302a32f7c7ed67523274aa8a92717",
		"checksum_sha1":"b733ec235ea57119172c8b044220e793446063fe",
		"id":123,
		"payload":"your payload data string"
	}
}

With every webhook event, JSON encoded data will be POSTed to the specified URL as the request's body with the Content-Type header set to application/json. Here are a few examples of what you can expect:

{
    "version":"1.0",
    "event":"video_recorded",
    "data":{
        "videoName":"STREAM_NAME",
        "audioCodec":"NellyMoser ASAO",
        "videoCodec":"H.264",
        "type":"flv",
        "id":123,
        "dateTime":"2016-03-03 15:51:44",
        "timeZone":"Europe/Bucharest",
        "payload":"your payload data string",
        "httpReferer":"http://site_from_where_video_was_recorded.com",
        "cam_name":"Logitech HD Pro Webcam C920 (046d:082d)",
        "mic_name":"Default",
        "ip":"91.16.93.181",
        "ua":"Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Mobile Safari/537.36"
    }
}
{
    "version":"1.0",
    "event":"video_converted",
    "data":{
        "videoName":"STREAM_NAME",
        "duration":7,
        "audioCodec":"AAC",
        "videoCodec":"H.264",
        "type":"mp4",
        "size":194373,
        "width":"320",
        "height":"240",
        "orientation":"landscape",
        "id":123,
        "dateTime": "2015-10-10 16:00:36",
        "timeZone":"Europe/Bucharest",
        "payload":"your payload data string"
    }
}
{
    "version":"1.0",
    "event":"video_copied_pipe_s3",
    "data":{
        "storedStatus":"stored successful",
        "videoName":"STREAM_NAME",
        "size":194373,
        "checksum_md5":"968302a32f7c7ed67523274aa8a92717",
        "checksum_sha1":"b733ec235ea57119172c8b044220e793446063fe",
        "id":123,
        "url":"https://addpipevideos.s3.amazonaws.com/b8e2f5bfd04a93b434bd8c740bff744d/STREAM_NAME.mp4",
        "rawRecordingUrl":"https://addpipevideos.s3.amazonaws.com/b8e2f5bfd04a93b434bd8c740bff744d/STREAM_NAME_raw.EXTENSION",
        "snapshotUrl":"https://addpipevideos.s3.amazonaws.com/b8e2f5bfd04a93b434bd8c740bff744d/STREAM_NAME.jpg",
        "filmstripUrl":"https://addpipevideos.s3.amazonaws.com/b8e2f5bfd04a93b434bd8c740bff744d/STREAM_NAME_filmstrip.jpg",
        "cdn":{"cdnRecordingUrl":"https://recordings-eu.addpipe.com/b8e2f5bfd04a93b434bd8c740bff744d/STREAM_NAME.mp4",
            "cdnRawRecordingUrl":"https://recordings-eu.addpipe.com/b8e2f5bfd04a93b434bd8c740bff744d/STREAM_NAME_raw.EXTENSION",
            "cdnSnapshotUrl":"https://recordings-eu.addpipe.com/b8e2f5bfd04a93b434bd8c740bff744d/STREAM_NAME.jpg",
            "cdnFilmstripUrl":"https://recordings-eu.addpipe.com/b8e2f5bfd04a93b434bd8c740bff744d/STREAM_NAME_filmstrip.jpg"},
        "bucket":"eu1-addpipe",
        "region":"eu-central-1",
        "payload":"your payload data string",
    }
}
{
    "version":"1.0",
    "event":"video_copied_ftp",
    "data":{
        "ftpUploadStatus":"upload success",
        "protocol":"ftp",
        "videoName":"STREAM_NAME",
        "type":"mp4",
        "size":493534,
        "checksum_md5":"968302a32f7c7ed67523274aa8a92717",
        "checksum_sha1":"b733ec235ea57119172c8b044220e793446063fe",
        "id":123,
        "payload":"your payload data string"
    }
}
{
    "version":"1.0",
    "event":"video_copied_s3",
    "data":{
        "s3UploadStatus":"upload success",
        "videoName":"STREAM_NAME",
        "type":"mp4",
        "size":493534,
        "checksum_md5":"968302a32f7c7ed67523274aa8a92717",
        "checksum_sha1":"b733ec235ea57119172c8b044220e793446063fe",
        "id":123,
        "url":"https://bucketname.s3.amazonaws.com/b8e2f5bfd04a93b434bd8c740bff744d/STREAM_NAME.mp4",
        "rawRecordingUrl":"https://bucketname.s3.amazonaws.com/b8e2f5bfd04a93b434bd8c740bff744d/STREAM_NAME_raw.EXTENSION",
        "snapshotUrl":"https://bucketname.s3.amazonaws.com/b8e2f5bfd04a93b434bd8c740bff744d/STREAM_NAME.jpg",
        "filmstripUrl":"https://bucketname.s3.amazonaws.com/b8e2f5bfd04a93b434bd8c740bff744d/STREAM_NAME_filmstrip.jpg",
        "bucket":"bucketname",
        "region":"us-east-1",
        "acl":"public-read",
        "payload":"your payload data string"
    }
}
{
    "version":"1.0",
    "event":"video_copied_dbox",
    "data":{
        "dboxUploadStatus":"upload success",
        "videoName":"STREAM_NAME",
        "type":"mp4",
        "size":493534,
        "checksum_md5":"968302a32f7c7ed67523274aa8a92717",
        "checksum_sha1":"b733ec235ea57119172c8b044220e793446063fe",
        "id":123,
        "payload":"your payload data string"
    }
}

Handling the data

With application/json the webhook will deliver the JSON data directly as the body of the POST request.

In PHP, for example, you can retrieve the raw JSON data with php://input:

$data = file_get_contents("php://input");
	$retrievedData = json_decode($data, true);

In .NET Request.InputStream retrieves the raw JSON data.

In Ruby on Rails request.raw_post can retrieve the raw JSON data.

With application/x-www-form-urlencoded the data is URL encoded in transit. This means that depending on the specific function of the programming language you are using, you will either be working with the raw URL-encoded data or the URL-decoded data. Also, keep in mind, with this content type, the JSON data will be sent as the value of a form parameter called payload.

Some examples:

In PHP, if you are using php://input for receiving the data, you will be working with the raw URL encoded data, but if you are using $_POST["payload"] for receiving the data, it will automatically be URL decoded.

In .NET Request.InputStream will receive the raw URL encoded data while Request.Form["payload"] will automatically URL decode the data.

In Ruby on Rails request.raw_post will receive the raw URL encoded data while params["payload"] will automatically URL decode the data.

Here's an example of the video_recorded event data being received in two possible ways.

Raw POST data (URL encoded) Value of payload var (URL decoded)
payload%7B%22version%22%3A%221.0%22%2C%22event
%22%3A%22video_recorded%22%2C%22data%22%3A%7B%22
videoName%22%3A%22vs1457013120534_862
%22%2C%22audioCodec%22%3A%22NellyMoser%20ASAO
%22%2C%22videoCodec%22%3A%22H.264%22%2C%22
type%22%3A%22FLV%22%2C%22orientation%22%3A%22
landscape%22%2C%22id%22%3A%22123%22%2C%22d
ateTime%22%3A%222016-03-03%2015%3A51%3A44%22%2C%22
timeZone%22%3A%22Europe%2FBucharest%22%2C%22
payload%22%3A%22your%20payload%20data%20string%22%2C%22
httpReferer%22%3A%22http%3A%2F%2F
your_site.com%22%7D%7D
{
	"version":"1.0",
	"event":"video_recorded",
	"data":{
		"videoName":"vs1457013120534_862",
		"audioCodec":"NellyMoser ASAO",
		"videoCodec":"H.264",
		"type":"FLV",
		"orientation":"landscape",
		"id":123,
		"dateTime":"2016-03-03 15:51:44",
		"timeZone":"Europe/Bucharest",
		"payload":"your payload data string",
		"httpReferer":"http://your_site.com"
	}
}

Every parameter explained

Let's talk about the information sent by these webhooks

version contains the current version of the Pipe's webhook API.

event contains the name of the event that triggered this call

videoName contains the name of the recording

duration contains the duration in seconds with two decimals

audioCodec contains the audio codec used in the original video (video_recorded) or in the final video (video_converted)

videoCodec contains the video codec used in the original video (video_recorded) or in the final video (video_converted). For an audio-only recording, it will have the value "no_video".

type

  • For the video_recorded event, it is the original recording file type.
  • For the video_converted event, it is the processed recording file type (mp4).
  • For the remaining webhook events (storage webhooks), it is the processed recording file type (mp4). If the mp4 output is disabled in the Transcoding Engine, the value is the original recording type.

type is the original recording file type. This can give you a clue on where this recording is coming from. For example, an iPhone will produce .mov files while the HTML5 recorder will produce .webm files.

size is the size in bytes

checksum_md5 contains the 32 characters md5 hash checksum of the final processed mp4 recording file. If the mp4 output is disabled in the Transcoding Engine, the value will be for the original recording.

checksum_sha1 contains the 40 characters sha-1 hash checksum of the final processed mp4 recording file. If the mp4 output is disabled in the Transcoding Engine, the value will be for the original recording.

width and height represent the size of the original recording. This can also give you a clue about the device the recording is coming from. For an audio-only recording, they will have the value 0.

orientation represents the orientation of the device when the recording was made. This could be either "landscape", "portrait", or "none". For an audio-only recording, it will have the value "none".

id is the ID of the recording as you will find it in your Pipe account dashboard and as it is stored in our database.

dateTime is the date and time relative to your set timezone at which the recording has been inserted in the database (video_recorded) or has finished converting (video_converted).

timeZone represents your selected time zone in the account settings.

url

  • For the video_copied_pipe_s3 event, it is the full path to where the MP4 recording is located on our S3 storage. If the MP4 output is not enabled this field will be an empty string.
  • For the video_copied_s3 event, it is the full path to where the MP4 recording is located on your S3 bucket. If the MP4 output is not enabled this field will be an empty string.

rawRecordingUrl

  • For the video_copied_pipe_s3 event, it is the full path to where the original recording is located on our S3 storage. If the original recording output is not enabled this field will be an empty string.
  • For the video_copied_s3 event, it is the full path to where the original recording is located on your S3 bucket. If the original recording output is not enabled this field will be an empty string.

snapshotUrl

  • For the video_copied_pipe_s3 event, it is the full path to where the snapshot is located on our S3 storage. If the snapshot output is not enabled this will be an empty string. For an audio-only recording, it will have the value "audio_file".
  • For the video_copied_s3 event, it is the full path to where the snapshot is located on your S3 bucket. If the snapshot output is not enabled this will be an empty string. For an audio-only recording, it will have the value "audio_only".

filmstripUrl

  • For the video_copied_pipe_s3 event, it is the full path to where the filmstrip is located on our S3 storage. If the filmstrip output is not enabled this will be an empty string. For an audio-only recording, it will have the value "audio_file".
  • For the video_copied_s3 event, it is the full path to where the filmstrip is located on your S3 bucket. If the filmstrip output is not enabled this will be an empty string. For an audio-only recording, it will have the value "audio_only".

cdn is the object containing all the CDN URLs for each of the recording outputs. Depending on the S3 origin region, the CDN links will have different specific subdomains: recordings-eu.addpipe.com (EU) or recordings-us.addpipe.com (US). The URLs are in sync with the S3 (origin) URLs. The CDN URLs for each output are named as follows: cdnRecordingUrl, cdnRawRecordingUrl, cdnSnapshotUrl, and cdnFilmstripUrl.

payload is the payload data in string format that can be set initially in the embed code of Pipe. Find out more at Sending custom data using payload. If no custom payload is sent this will be an empty string.

storedStatus is the status of the upload to Pipe's complimentary storage.

ftpUploadStatus is the status of the upload through SFTP or FTP(S).

s3UploadStatus is the status of the S3 upload.

bucketname is the name of the S3 bucket.

region is the region of the S3 bucket.

acl is the Amazon S3 Canned Access Control List.

dboxUploadStatus is the status of the Dropbox upload.

httpReferer is the link to the site from which the recording was made.

Webhook schedule

The Webhook schedule section shows the list of events we've sent data for. Events from the last 72 hours are shown for Standard accounts and from the last 30 days for PRO accounts.

If you have a webhook configured to fire for more than one event, each event will show up in this section. Events are added to this list as the recording goes through our processing pipeline.

If the first attempt of firing the webhook for an event runs into an issue/problem, you can always use this section to re-send the webhook for that particular event. You can also re-send webhooks if you just need them to be sent more than once. For each firing of a webhook, a log entry will be created in the webhook logs section.

The following information is available in the webhook schedule table:

Recording ID The ID of the recording for which the webhook was scheduled
Date & time The date and time the first attempt was made
Event type The event that triggered the webhook
Last HTTP status The status of the last webhook firing
URL The URL of your webhook handler
Request Headers & Request Body The request headers and request body that were sent when the webhook was fired
Retry A retry button for the particular webhook
Attempts The number of firing attempts that were made for the particular webhook

Webhook logs

The Webhook logs section shows the logs associated with events from the last 72 hours (Standard accounts) or 30 days (PRO accounts). Each attempt to push data to your webhook URL for a particular event has an entry here.

For example, a recording was converted and the video_converted webhook that you have set up has been immediately scheduled and fired but for some reason, your webhook code fails to run correctly and the web server ends up responding with a 500 Internal Error HTTP status. You re-send the webhook from the schedule section. This time, the webhook fires, and your webhook handling code correctly processes the data. In this example, a total of 2 log entries will be created for the same webhook event: the one that failed and the 2nd one that resulted in a successful execution of your code.

The following information is available in the webhook logs table:

Date & time The date and time at which the log entry was created
Recording ID The ID of the recording for which the webhook was fired and logged
Connection status The initial webhook call status. A ✓ will be shown if everything was successful, otherwise, an error code will be shown.
SSL status The verification status for the certificate of the server to which the webhook call is made. A ✓ will be shown if everything was successful, otherwise, an SSL status code will be shown.
HTTP status The HTTP status resulted from the webhook firing
URL The URL of your webhook handler
Response time The time (in milliseconds ) it took for your webhook handler to respond to the webhook firing
Event type The event that triggered the webhook
Request Headers & Request Body The request headers and request body that were sent when the webhook was fired
Response Headers & Response Body The response headers and response body that were received from your webhook handler

SSL status codes


The table in the webhook logs section has a column named "SSL status". This is what the numeric status codes in that column (if any) mean:

0ok the operation was successful
2unable to get issuer certificate
3unable to get certificate CRL
4unable to decrypt the certificate's signature
5unable to decrypt CRL's signature
6unable to decode issuer public key
7certificate signature failure
8CRL signature failure
9the certificate is not yet valid
10certificate has expired
11CRL is not yet valid
12CRL has expired
13format error in the certificate's notBefore field
14format error in the certificate's notAfter field
15format error in CRL's lastUpdate field
16format error in CRL's nextUpdate field
17out of memory
18self-signed certificate
19self-signed certificate in the certificate chain
20unable to get local issuer certificate
21unable to verify the first certificate
22certificate chain too long
23certificate revoked
24invalid CA certificate
25path length constraint exceeded
26unsupported certificate purpose
27certificate not trusted
28certificate rejected
29subject issuer mismatch
30authority and subject key identifier mismatch
31authority and issuer serial number mismatch
32key usage does not include certificate signing
50application verification failure

Signing webhooks


Pipe automatically signs the webhook requests so you can (optionally) verify that the requests are coming from Pipe and not a third party. This is useful if your application exposes sensitive data, but it is not required. It just adds an additional layer of protection.

Pipe adds an additional HTTP header with the webhook POST requests, X-Pipe-Signature, which contains the specific signature for the request. To verify this request, you will need to generate a signature using the same key that Pipe uses (the one in your account webhook tab) and compare the value obtained with the value sent via the header.

Getting your webhook key

In the webhook tab of your account, a key is automatically generated. You can use this one or reset it to generate a new one at any time. Pipe will automatically use the newest one.

Generating your own signature for comparison

In the code that receives and processes the webhook request:

  1. Create a string of the webhook's URL, exactly as you entered it in Pipe. Pipe will always sign the webhook requests with the exact URL, so any small difference will prevent the signature from validating.
  2. Append the JSON data received via webhook:
    • the actual request body with Content-type: application/json webhooks
    • the value of the payload key with Content-type: application/x-www-form-urlencoded webhooks
  3. Hash the resulting string with HMAC-SHA1, using the webhook key from your account, and generate a binary signature. Pipe uses binary signatures so be careful not to generate a hexadecimal signature.
  4. Base64 encode the binary signature.
  5. Compare the generated signature with the one provided by X-Pipe-Signature HTTP header.

Example of a PHP implementation of the steps above

/**
* Generates a base64-encoded signature for a Pipe webhook request.
* @param string $key the webhook's key
* @param string $url the webhook url
* @param string $jsonData the data in JSON format
*/

function generateSignature($key, $url,  $jsonData){
  $data_to_sign = $url . $jsonData;
  return base64_encode(hash_hmac('sha1', $data_to_sign, $key, true));
}

$key = "";
$url = "";
if($_SERVER['CONTENT_TYPE']=="application/json"){
  $data=file_get_contents("php://input");
}else if ($_SERVER['CONTENT_TYPE']=="application/x-www-form-urlencoded"){
  $data=$_POST["payload"];
}
echo generateSignature($key,$url,$data);

Example of a Python (using Flask) implementation of the steps above

import base64
import hashlib
import hmac
import json
from flask import Flask, request

def generateSignature(key: str, url: str, jsonData :str) -> str:
    """
    Generates a base64-encoded signature for a Pipe webhook request.

    :param str key: the webhook's key
    :param str url: the webhook url
    :param str jsonData the data in JSON format
    :return: the signature of the request
    :rtype: str
    """
    data_to_sign = url + jsonData
    sign = base64.b64encode(hmac.new(key.encode(), data_to_sign.encode(), digestmod=hashlib.sha1).digest()).decode()
    return sign

webhook_key = "key"
webhook_url = "https://website.com/custom-route"

app = Flask(__name__)
# Define a POST route on "/custom-route" to get all POST request comming to our webhook_url
@app.route('/custom-route', methods = ['POST'])
def customFunction():
    sign = ""
    contentType = request.headers.get('Content-Type')
    # If content type is application/json the data is in the body as a JSON
    if contentType == 'application/json':
        # Remove extra spaces and newlines when we convert JSON to string
        data = json.dumps(request.json, separators=(',', ':'))
        sign = generateSignature(webhook_key, webhook_url, data)
    # If content type is application/x-www-form-urlencoded the data is in the POST from with the key "payload"
    elif contentType == 'application/x-www-form-urlencoded':
        data = request.form['payload']
        sign = generateSignature(webhook_key, webhook_url, data)
    print(sign)
    return sign

Handling webhook data on the server side


Handling webhook data on the server is very simple, here's an example in PHP:

//we get the JSON data from the POST, that is being sent as is
$body = file_get_contents("php://input");

//the data is JSON encoded, so we must decode it in an associative array
$webhookData = json_decode($body, true);

//you can get the webhook type by accessing the event element in the array
$type = $webhookData["event"];

//if you wish to get the name of the video you simply access it like this
$vidName = $webhookData["data"]["videoName"]

Decoding the JSON data of the video_converted webhook, like in the example above, will make it easily accessible in an associative array similar to the one shown here:

Array
(
    [version] => 1.0
    [event] => video_converted
    [data] => Array
        (
            [videoName] => vsrtc1501002174_491
            [duration] => 3.48
            [audioCodec] => AAC
            [videoCodec] => H.264
            [type] => mp4
            [size] => 442101
            [width] => 1280
            [height] => 720
            [orientation] => landscape
            [id] => 451464
            [dateTime] => 2017-07-25 20:03:05
            [timeZone] => Europe/Bucharest
            [payload] => {"userId":"55a95eeb936dd30100e0aff6","jobId":"55a7e6555f1bdc010014d6a1", "email":"asdasd@adasdae.com", "link":"https://www.amazon.com/D-G-P-4-W/dp/ref=as_li_ss_tl?ie=UTF8"}
        )

)
//we get the URL encoded payload data from the POST
$payload = $_POST["payload"];

//the data is JSON encoded, so we must decode it in an associative array
$webhookData = json_decode($payload, true);

//you can get the webhook type by accessing the event element in the array
$type = $webhookData["event"];

//if you wish to get the name of the video you simply access it like this
$vidName = $webhookData["data"]["videoName"]

Decoding the JSON data of the video_converted webhook, like in the example above, will make it easily accessible in an associative array similar to the one shown here:

Array
(
    [version] => 1.0
    [event] => video_converted
    [data] => Array
        (
            [videoName] => vsrtc1501002174_491
            [duration] => 3.48
            [audioCodec] => AAC
            [videoCodec] => H.264
            [type] => mp4
            [size] => 442101
            [width] => 1280
            [height] => 720
            [orientation] => landscape
            [id] => 451464
            [dateTime] => 2017-07-25 20:03:05
            [timeZone] => Europe/Bucharest
            [payload] => {"userId":"55a95eeb936dd30100e0aff6","jobId":"55a7e6555f1bdc010014d6a1", "email":"asdasd@adasdae.com", "link":"https://www.amazon.com/D-G-P-4-W/dp/ref=as_li_ss_tl?ie=UTF8"}
        )

)

Attach custom data to recordings


You can attach custom data to each recorder. The custom data will then be attached to all the recordings made with that recorder and it will be passed back to you through webhooks. This is very useful when you wish to tunnel data - like a user id or a submission id - from your embed code all the way to your webhook receiving code. Custom data can be a simple string or a JSON object.

The custom data will also be shown in the recordings list in the Pipe account dashboard. In the example below, the custom payload data was "user-id:30785".

Custom data shown in the payload of a recording from recordings list in the Pipe account dashboard

Sending a simple string as custom data

We will use flashvar's payload property to attach custom data to the recordings made with this recorder.

Here's how to add the payload property to the flashvar object. This is the default embed code:

var flashvars = {qualityurl: "avq/240p.xml",accountHash:"your_account_hash",showMenu:"true", mrt:120};

This is an embed code with the payload property added:

var flashvars = {qualityurl: "avq/240p.xml",accountHash:"your_account_hash",showMenu:"true", mrt:120, payload:"your_payload_data_string"};

We will use the pipe-payload attribute to attach custom data to the recordings made with this recorder.

Here's how to add the pipe-payload attribute to the <piperecorder> tag. This is the default embed code:

<piperecorder id="test-div" pipe-width="640" pipe-height="510" pipe-qualityurl="avq/480p.xml" pipe-accounthash="your_account_hash" pipe-eid="1" pipe-showmenu="1" pipe-mrt="120"></piperecorder>

This is an embed code with the pipe-payload attribute added:

<piperecorder id="test-div" pipe-width="640" pipe-height="510" pipe-qualityurl="avq/480p.xml" pipe-accounthash="your_account_hash" pipe-eid="1" pipe-showmenu="1" pipe-mrt="120" pipe-payload="your_payload_data_string" ></piperecorder>

We will use the JavaScript object's payload property to attach custom data to the recordings made with this recorder.

Here's how to add the payload property to your custom JavaScript object. This is a generic JavaScript object without the payload property:

var YOUR_CUSTOM_OBJECT_NAME = {qualityurl: "avq/240p.xml",accountHash:"your_account_hash",showMenu:1, mrt:120};

Here's the same JavaScript object but with the payload property added:

var YOUR_CUSTOM_OBJECT_NAME = {qualityurl: "avq/240p.xml",accountHash:"your_account_hash",showMenu:1, mrt:120, payload:"your_payload_data_string"};

Sending a JSON object as custom data

To send a JSON object as custom data you'll have to send it as a string through the payload property (or pipe-payload attribute if using the 2.0 HTML embed code).

You have two formatting options in order to send the JSON correctly:

  • enclose the JSON in quotes and write the JSON normally: payload:'{"a":"b"}'
  • enclose the JSON in double quotes and escape the double quotes from inside the JSON payload:"{\"a\":\"b\"}"

Receiving the data

The custom data you've included in the embed code will be sent back to you with (all) the webhooks that fire for the events associated with the recordings made with that recorder. The data will be available as the value of the payload property of the data object:

payload={
	"version":"1.0",
	"event":"video_converted",
	"data":{
		"videoName":"recording name here",
		"duration":"7.56",
		"audioCodec":"AAC",
		"videoCodec":"H.264",
		"type":"mp4",
		"size":194373,
		"width":"320",
		"height":"240",
		"orientation":"landscape",
		"id":123,
		"dateTime": "2015-10-10 16:00:36",
		"timeZone":"Europe/Bucharest",
		"payload":"your_payload_data_string"
	}
}

JavaScript API v1.0


You can use this API in the browser, on the page where the Pipe recording client is embedded to check the status of the recording client & recording process using control functions and events. The 1.0 version of the JavaScript API can be used with recording clients embedded using the 1.0 Embed Code.

The JavaScript API v1.0 has 3 components:

JavaScript Control API v1.0 (Desktop recorder)

The JavaScript Control API allows you to control Pipe's desktop recorder using JavaScript.

With it, you can for example create your own UI by hiding the lower menu of the desktop Pipe recorder and control the recording process through the JS Control API using your own buttons.

Controlling the recorder through JavaScript when using the 1.0 embed code

The 1.0 version of the embed code supports only 1 recorder on the page represented by the VideoRecorder object. You can tell it to start recording by calling the record() method on the VideoRecorder object like this:

<input type="button" class="btn" value="Record" id="recordbtn" />
<script>
//wait for onRecorderReady before attaching the click event to the button above
function onRecorderReady(recorderId,recorderType){
 document.getElementById("recordbtn").onclick = function (){
    document.VideoRecorder.record();
 }
}
</script>

Full list of control methods you can use when using the 1.0 embed code

Here is the full list of methods that you can call on the Pipe recorder and that make up the JS Control API:

record();

Description: This call will trigger the recorder to record a video or audio stream or to rerecord one if one is already recorded (equivalent to pressing the RECORD button).

It should be executed only after:

  1. onCamAccess() is called by Pipe and the value of allowed is true
  2. onRecorderReady() (Embed Code v1.0) or onReadyToRecord() (Embed Code v2.0) is called by Pipe

If executed before the above 2 events the initial recording screen OR mic/cam device selection screens might be shown by Pipe in which case calling record() will do nothing.

Return Value: none

stopVideo();

Description: This call will trigger the Pipe desktop client to stop the recording of a stream if a recording is in progress.

Return Value: none

playVideo();

Description: This call will play the recorded stream if there is one. This will not be available during or before the recording process (equivalent to pressing the PLAY button).

Return Value: none

pause();

Description: This call will pause the video recording that is currently playing. The call will not do anything if a video recording is not playing. The call is equivalent to pressing the PAUSE PLAYBACK button. Calling the function a second time will not do anything, you need to call playVideo() to resume playback.

Return Value: none

save();

Description: If recording autosaving is disabled you can use this call to save the desired recording (equivalent to pressing the Save button). This will not be available if recording autosaving is enabled.

Return Value: none

getStreamTime();

Description: This will return the time of the played or the currently recording stream.

Return Value: a number with the current length of the recording (if called during the recording process)

getPlaybackTime();

Description: This will return the current time of the recording during playback.

Return Value: a number with the current time of the recording if called during the playback process or 0 if called outside of playback

getStreamName();

Description: This call will return the stream name at any time but beware that the name of the stream might not be set at the time of the call. This is useful when you need the name of the stream during the recording.

Return Value: a string, the stream name WITHOUT the file extension. If the stream name is not set it will return an empty string.

Removing the Pipe Recorder from page (Embed Code 1.0)

When the Pipe HTML5 recorder is used and it's part of a modal window that the user closes (either through CSS' display: none or by removing the modal DIV from the DOM) the webcam will continue to be accessed and its light will continue to be turned on.

For this scenario, we've built the following JS function that closes the media server connection, turns off the webcam access, and clears the contents of the #hdfvr-content DIV returning the HTML source code to its initial state:removePipeRecorder();.

Execute it before closing the modal window.

JavaScript Events API v1.0 (Desktop recorder)

Every time something happens with the desktop recorder (a button is pressed, the connection succeeds, etc.), the recorder will execute a specific JavaScript function with information about the event.

You can add these event functions to your HTML/JS app and extend them with your own code to do your bidding.

Here are some of the things you can do by extending these event functions:

  • show a message, a warning, or a timer on the HTML page
  • activate an input field or button in the HTML page
  • redirect the user to another page

When using the v1 embed code you can just add these JS event functions to your HTML page. The Pipe recorder client will detect their presence and execute them when needed.

Try adding the code below immediately after your v1 Pipe recorder embed code:

<script>
function onRecorderInit(recorderId){
    var args = Array.prototype.slice.call(arguments);
    alert("onRecorderInit("+args.join(', ')+")");
}
</script>

An alert will pop up on your web page whenever the Pipe recording client finishes initializing. You can replace onRecorderInit(recorderId) with any of the event functions below.

The desktop recorder has 2 different set of events and corresponding event functions:

Event functions executed when recording a new video using the desktop recorder with embed code 1.0

This is the list of JavaScript event functions the Pipe client will execute when the user is recording from a desktop device using his webcam or microphone.

onRecorderInit(recorderId);

Description: Triggers after the initial [Record Video] or [Record Audio] screen is drawn.

Passed Parameters:
  • recorderId - the value of recorderId property of flashvars object in the embed code

Example:

function onRecorderInit(recorderId){
	var args = Array.prototype.slice.call(arguments);
	alert("onRecorderInit("+args.join(', ')+")");
}
onRecorderReady(recorderId, recorderType);

Description: The Pipe recorder is ready to start recording videos (the interface has been built, you have allowed access to your webcam and you can see yourself in the browser)

Passed Parameters:
  • recorderId - the value of recorderId property of flashvars object in the embed code
  • recorderType - a string representing the type of the recorder (flash or HTML5).

Example 1: show a browser alert when onRecorderReady is executed

<script>
function onRecorderReady(recorderId, recorderType){
    var args = Array.prototype.slice.call(arguments);
    alert("onRecorderReady("+args.join(', ')+")");
}
</script>

Example 2: wait for onRecorderReady before allowing an HTML button to trigger the recording process

<input type="button" class="btn" value="Record" id="recordbtn" />
<script>
//wait for onRecorderReady before adding the onclick event to the button above
function onRecorderReady(recorderId,recorderType){
 console.log("onRecorderReady executed")
 //when the button is clicked
 document.getElementById("recordbtn").onclick = function (){
      //trigger the recording process
      document.VideoRecorder.record();
 }
}
</script>
userHasCamMic(cam_number,mic_number, recorderId);

Description: Pipe detects the number of cams and mics a user has

Passed Parameters:
  • cam_number - number of webcam drivers the user has installed on his computer
  • mic_number - number of sound cards and sound sources the user has installed on his computer
  • recorderId - the value of recorderId property of flashvars object in the embed code

Example:

function userHasCamMic(cam_number,mic_number, recorderId){
	var args = Array.prototype.slice.call(arguments);
	alert("userHasCamMic("+args.join(', ')+")");
}
btRecordPressed(recorderId);

Description: RECORD button is pressed. This event does NOT mark the exact start of the recording. See onRecordingStarted.

Passed Parameters:
  • recorderId - the value of recorderId property of flashvars object in the embed code

Example:

function btRecordPressed(recorderId){
	var args = Array.prototype.slice.call(arguments);
	alert("btRecordPressed("+args.join(', ')+")");
}
btStopRecordingPressed(recorderId);

Description: STOP RECORD button is pressed

Passed Parameters:
  • recorderId - the value of recorderId property of flashvars object in the embed code

Example:

function btStopRecordingPressed(recorderId){
	var args = Array.prototype.slice.call(arguments);
	alert("btStopRecordingPressed("+args.join(', ')+")");
}
btPlayPressed(recorderId);

Description: PLAY button is pressed

Parameters:

  • recorderId - the value of recorderId property of flashvars object in the embed code

Example:

function btPlayPressed(recorderId){
	var args = Array.prototype.slice.call(arguments);
	alert("btPlayPressed("+args.join(', ')+")");
}
btPausePressed(recorderId);

Description: PAUSE button is pressed

Passed Parameters:
  • recorderId - the value of recorderId property of flashvars object in the embed code

Example:

function btPausePressed(recorderId){
	var args = Array.prototype.slice.call(arguments);
	alert("btPausePressed("+args.join(', ')+")");
}
onUploadDone(streamName, streamDuration, userId, recorderId, audioCodec, videoCodec, fileType, audioOnly, location);

Description: Recorded data finishes streaming/uploading to the media server

Passed Parameters:
  • streamName - a string representing the name of the stream WITHOUT the file extension
  • streamDuration - the duration of the recording/audio file in seconds but accurate to the millisecond (like this: 4.322)
  • userId - the userId sent via flash vars
  • recorderId - the value of recorderId property of flashvars object in the embed code
  • audioCodec - the audio codec used for the recording
  • videoCodec - the video codec used for the recording
  • fileType - the initial file extension for the recording
  • audioOnly - true if it is an audio-only recording, false otherwise
  • location - will take one of 4 values:
    1. eu2-addpipe.s3.nl-ams.scw.cloud
    2. us1-addpipe.s3-us-west-1.amazonaws.com
    3. us2-addpipe.s3.us-east-1.amazonaws.com
    4. eu1-addpipe.s3.eu-central-1.amazonaws.com

The location contains the subdomain of the regional storage bucket where the final recording and snapshot will be stored by Pipe. For the EU2 region, for example, the recording will be stored at https://eu2-addpipe.s3.nl-ams.scw.cloud/ACCOUNT_HASH/FILE_NAME.mp4. The snapshot will be at the same location but with the .jpg extension. If Do Not Store Files is turned on, the final processed files will not be pushed to this storage location.

Example:

function onUploadDone(streamName, streamDuration, userId, recorderId, audioCodec, videoCodec, fileType, audioOnly,location){
	var args = Array.prototype.slice.call(arguments);
	alert("onUploadDone("+args.join(', ')+")");
}
onUploadProgress(percent);

Description: Triggers every time upload progress is made.

Passed Parameters:
  • percent - the percent at which the upload progress is at

Example:

function onUploadProgress(percent){
	var args = Array.prototype.slice.call(arguments);
	alert("onUploadProgress("+args.join(', ')+")");
}
onCamAccess(allowed, recorderId);

Description:

onCamAccess is triggered:

  1. for macOS, after clicking the Allow or Don't Allow buttons in the operating system's privacy dialog box that's asking permission for camera and microphone access. The dialog box will be brought up by the operating system the first time the recorder tries to access the camera or the microphone.
  2. after clicking the Allow or Block (Don't Allow for Safari) buttons in the browser's privacy dialog box asking permission for camera and microphone access. The dialog box will be brought up by the browser the first time the recorder tries to access the camera or the microphone.
  3. automatically if a previous choice (Allow or Block) made by the user is persistent.

Here is an example of Chrome's privacy dialog box:

Chrome permission dialog box to allow Pipe to use camera/microphone

And here is how macOS warns the user for an additional time that Firefox would like to access the camera and microphone.

macOS permission dialog box to allow Pipe to use camera/microphone Passed Parameters:
  • allowed
    • is true if:
      • the user clicked the [Allow] button OR
      • clicked [Allow] in a previous session and that choice is persistent (in which case no additional dialog box is shown).
    • and false if
      • the user clicked the [Block] button OR
      • clicked [Block] in a previous session and that choice is persistent (in which case no additional dialog box is shown).
  • recorderId - the value of recorderId property of flashvars object in the embed code

Example:

function onCamAccess(allowed, recorderId){
	var args = Array.prototype.slice.call(arguments);
	alert("onCamAccess("+args.join(', ')+")");
}
onPlaybackComplete(recorderId);

Description: Pipe finishes playback of recording stream

Passed Parameters:
  • recorderId - the value of recorderId property of flashvars object in the embed code

Example:

function onPlaybackComplete(recorderId){
	var args = Array.prototype.slice.call(arguments);
	alert("onPlaybackComplete("+args.join(', ')+")");
}
onRecordingStarted(recorderId);

Description: the Pipe desktop client started recording. This event is called ONLY if there's data to record and, when it is called, it will be called with a 200-220ms delay after the record button is pressed and the btRecordPressed event is triggered.

Passed Parameters:
  • recorderId - the value of recorderId property of flashvars object in the embed code

Example:

function onRecordingStarted(recorderId){
	var args = Array.prototype.slice.call(arguments);
	alert("onRecordingStarted("+args.join(', ')+")");
}
onConnectionClosed(recorderId);

Description:the connection to the media server has failed completely (the connection could not be reestablished even after the 30 reconnection attempts in the HTML5 recorder)

Passed Parameters:
  • recorderId - the value of recorderId property of flashvars object in the embed code

Example:

function onConnectionClosed(recorderId){
	var args = Array.prototype.slice.call(arguments);
	alert("onConnectionClosed("+args.join(', ')+")");
}
onConnectionStatus(status, recorderId);

Description: Called by the Pipe desktop client for every connection event

Passed Parameters:
  • status - the actual connection status:

HTML5 only

• connected
- The connection is live
• disconnected: ping timeout
- When the client loses Internet connection.
• disconnected: io server disconnect
- When the media server is shut down.
• disconnected: io client disconnect
- The socket was manually disconnected.
• disconnected: transport close
- The connection was closed (for example the user has lost connection, or the network was changed from WiFi to 4G).
• disconnected: transport error
- The connection has encountered an error (for example the server was killed during an HTTP long-polling cycle)
• error
- Thrown when an error occurs.
• connect_timeout
- Thrown when connection timeouts.
• reconnecting
- Actively trying to reconnect. A total of 30 reconnect attempts will be made, each attempt at an interval between 1000 ms - 5000 ms.
• connect_error: (+ error)
- Thrown after a connection error.
• reconnect_error
- Thrown only after a reconnecting attempt when the reconnection attempt resulted in an error.
• reconnect_failed
- Reconnection has completely failed after a total number of 30 attempts were made. The client is considered fully disconnected.
• reconnected
- Thrown only after a reconnecting attempt when the reconnection was successful.

recorderId - the value of recorderId property of flashvars object in the embed code

Example:

function onConnectionStatus(status, recorderId){
	var args = Array.prototype.slice.call(arguments);
	alert("onConnectionStatus("+args.join(', ')+")");
}
onMicActivityLevel(recorderId, currentActivityLevel);

Description: the function is called by the Pipe desktop client every 10th of a second (100 milliseconds) with the current microphone level.

Parameters:

  • recorderId - the value of recorderId property of flashvars object in the embed code
  • currentActivityLevel - The amount of sound the microphone is detecting in numeric values between 0 and 100. From our experience, it's hard to get values over 50 though.

Example:

function onMicActivityLevel(recorderId, currentActivityLevel){
	var args = Array.prototype.slice.call(arguments);
	alert("onMicActivityLevel("+args.join(', ')+")");
}
onSaveOk(streamName, streamDuration, userId, cameraName, micName, recorderId, audioCodec, videoCodec, fileType, videoId, audioOnly, location);

Description: The recording data has been fully streamed to our media servers for further processing.

This is not a confirmation that the recorded file is ready for delivery as the recording will take a few seconds to be processed (conversion to .mp4, snapshot extraction, rotation, push to storage location).

Use Pipe's webhooks to be notified when the recording is fully processed and ready for delivery.

Passed Parameters:
  • streamName - a string representing the name of the stream WITHOUT the file extension
  • streamDuration - the duration of the recorded video/audio file in seconds but accurate to the millisecond (like this: 4.322)
  • userId - variable sent via flash vars
  • cameraName - the name of the webcam driver the user has selected for capturing video data
  • micName - the name of the sound card/mic driver the user has selected for capturing audio data
  • recorderId - the value of recorderId property of flashvars object in the embed code
  • audioCodec - the audio codec used for the recording
  • videoCodec - the video codec used for the recording
  • fileType - the initial file extension for the recording
  • videoId - a string representing the recording id in Pipe's database (same recording id that is sent through the webhook)
  • audioOnly - true if it is an audio-only recording, false otherwise
  • location - will take one of 4 values:
    1. eu2-addpipe.s3.nl-ams.scw.cloud
    2. us1-addpipe.s3-us-west-1.amazonaws.com
    3. us2-addpipe.s3.us-east-1.amazonaws.com
    4. eu1-addpipe.s3.eu-central-1.amazonaws.com

The location contains the subdomain of the regional storage bucket where the final recording and snapshot will be stored by Pipe. For the EU2 region, for example, the recording will be stored at https://eu2-addpipe.s3.nl-ams.scw.cloud/ACCOUNT_HASH/FILE_NAME.mp4. The snapshot will be at the same location but with the .jpg extension. If Do Not Store Files is turned on, the final processed files will not be pushed to this storage location.

Example:

function onSaveOk(streamName, streamDuration, userId, cameraName, micName, recorderId, audioCodec, videoCodec, fileType, videoId, audioOnly,location){
	var args = Array.prototype.slice.call(arguments);
	alert("onSaveOk("+args.join(', ')+")");
}

Event functions executed when uploading an existing file using the desktop recorder with embed code 1.0

Below is the list of JavaScript functions the Pipe client will execute when the user is UPLOADING an existing video using the desktop recorder. You can turn on the feature from your embed code or from the Pipe account dashboard when generating an embed code, details here.

onDesktopVideoSelected(filename, filetype, audioOnly);

Description: The user has selected a recording from the computer's library and the recording is ready for upload.

Passed Parameters:
  • filename - the (automatically generated) name of the recording without extension
  • filetype - the file extension of the uploaded video, it can be mp4, mov, 3gp, m4v, mkv, avi, mp3, wma, etc.
  • audioOnly - true if we're expecting an audio-only recording, false otherwise

Example:

function onDesktopVideoSelected(filename,filetype, audioOnly){
	var args = Array.prototype.slice.call(arguments);
	alert("onDesktopVideoSelected("+args.join(', ')+")");
onDesktopVideoSubmitted();

Description: Triggered when the recording is auto-submitted.

Passed Parameters:
  • None

Example:

function onDesktopVideoSubmitted(){
	var args = Array.prototype.slice.call(arguments);
	alert("onDesktopVideoSubmitted("+args.join(', ')+")");
}
onDesktopVideoUploadSuccess(filename, filetype, videoId, audioOnly, location);

Description: The recording has finished uploading successfully.

Passed Parameters:
  • filename - the automatically generated name of the recording
  • filetype - the file extension of the uploaded video, it can be mp4, mov, 3gp, m4v, mkv, avi, mp3, wma, etc.
  • videoId - a string representing the recording id in Pipe's database (same recording id that is sent through the webhook)
  • audioOnly - true if it is an audio-only recording, false otherwise
  • location - will take one of 4 values:
    1. eu2-addpipe.s3.nl-ams.scw.cloud
    2. us1-addpipe.s3-us-west-1.amazonaws.com
    3. us2-addpipe.s3.us-east-1.amazonaws.com
    4. eu1-addpipe.s3.eu-central-1.amazonaws.com

The location contains the subdomain of the regional storage bucket where the final recording and snapshot will be stored by Pipe. For the EU2 region, for example, the recording will be stored at https://eu2-addpipe.s3.nl-ams.scw.cloud/ACCOUNT_HASH/FILE_NAME.mp4. The snapshot will be at the same location but with the .jpg extension. If Do Not Store Files is turned on, the final processed files will not be pushed to this storage location.

Example:

function onDesktopVideoUploadSuccess(filename,filetype,videoId,audioOnly,location){
	var args = Array.prototype.slice.call(arguments);
	alert("onDesktopVideoUploadSuccess("+args.join(', ')+")");
}
onDesktopVideoUploadProgress(percent);

Description: Triggers every time upload progress is made.

Passed Parameters:
  • percent - the percent at which the upload progress is at

Example:

function onDesktopVideoUploadProgress(percent){
	var args = Array.prototype.slice.call(arguments);
	alert("onDesktopVideoUploadProgress("+args.join(', ')+")");
}
onDesktopVideoUploadFailed(error);

Description: There was an error while uploading the recording.

Passed Parameters:
  • error - The error thrown when the upload failed to complete

Example:

function onDesktopVideoUploadFailed(error){
	var args = Array.prototype.slice.call(arguments);
	alert("onDesktopVideoUploadFailed("+args.join(', ')+")");
}

JavaScript Events API v1.0 (Mobile native recorder)

Every time something happens with the mobile native recorder (a video is starting to upload, an upload has finished, etc.) the recorder will execute a specific JavaScript function with information about the event.

You can add these mobile native recorder event functions to your HTML/JS app and extend them with your own code to do your bidding.

When using the v1.0 embed code you can just add these JS event functions to your HTML page. The Pipe mobile native recorder will detect their presence and execute them when needed.

Try adding the code below immediately after your v1 Pipe recorder embed code:

<script>
function onVideoRecorded(filename,filetype, audioOnly){
    var args = Array.prototype.slice.call(arguments);
    alert("onVideoRecorded("+args.join(', ')+")");
}
</script>

An alert will pop up on your web page whenever you close the iOS/Android-controlled recording UI.

You can replace onVideoRecorded(filename,filetype, audioOnly) in the example above with any of the mobile event functions below.

onVideoRecorded(filename, filetype, audioOnly);

Description: The user has recorded a new video or chosen an existing video from the device's library and the video is ready for upload.

Passed Parameters:
  • filename - the (automatically generated) name of the recording without extension
  • filetype - the file extension of the uploaded video, it can be mp4, mov, 3gp, m4v, etc.
  • audioOnly - true if we're expecting an audio-only recording, false otherwise

Example:

function onVideoRecorded(filename,filetype, audioOnly){
	var args = Array.prototype.slice.call(arguments);
	alert("onVideoRecorded("+args.join(', ')+")");
onClickUpload();

Description: Triggered when the upload button is clicked.

Passed Parameters:
  • None

Example:

function onClickUpload(){
	var args = Array.prototype.slice.call(arguments);
	alert("onClickUpload("+args.join(', ')+")");
}
onVideoUploadSuccess(filename, filetype, videoId, audioOnly, location);

Description: The new or existing recording has finished uploading successfully.

Passed Parameters:
  • filename - the automatically generated name of the recording
  • filetype - the file extension of the uploaded video, it can be mp4, mov, 3gp, m4v, etc.
  • videoId - a string representing the recording id in Pipe's database (same recording id that is sent through the webhook)
  • audioOnly - true if it is an audio-only recording, false otherwise
  • location - will take one of 4 values:
    1. eu2-addpipe.s3.nl-ams.scw.cloud
    2. us1-addpipe.s3-us-west-1.amazonaws.com
    3. us2-addpipe.s3.us-east-1.amazonaws.com
    4. eu1-addpipe.s3.eu-central-1.amazonaws.com

The location contains the subdomain of the regional storage bucket where the final recording and snapshot will be stored by Pipe. For the EU2 region, for example, the recording will be stored at https://eu2-addpipe.s3.nl-ams.scw.cloud/ACCOUNT_HASH/FILE_NAME.mp4. The snapshot will be at the same location but with the .jpg extension. If Do Not Store Files is turned on, the final processed files will not be pushed to this storage location.

Example:

function onVideoUploadSuccess(filename,filetype,videoId,audioOnly,location){
	var args = Array.prototype.slice.call(arguments);
	alert("onVideoUploadSuccess("+args.join(', ')+")");
}
onVideoUploadProgress(percent);

Description: Triggers every time upload progress is made.

Passed Parameters:
  • percent - the percent at which the upload progress is at

Example:

function onVideoUploadProgress(percent){
	var args = Array.prototype.slice.call(arguments);
	alert("onVideoUploadProgress("+args.join(', ')+")");
}
onVideoUploadFailed(error);

Description:There was an error while uploading the recording.

Passed Parameters:
  • error - The error thrown when the upload failed to complete

Example:

function onVideoUploadFailed(error){
	var args = Array.prototype.slice.call(arguments);
	alert("onVideoUploadFailed("+args.join(', ')+")");
}

JavaScript API v2.0


You can use this API in the browser, on the page where the Pipe recording client is embedded to check the status of the recording client & recording process using control functions and events. The 2.0 version of the JavaScript API can be used with recording clients embedded using the 2.0 Embed Code.

The JavaScript API v2.0 has 3 main components:

JavaScript Control API v2.0 (Desktop recorder)

The JavaScript Control API allows you to control Pipe's desktop recorder using JavaScript.

With it, you can for example create your own UI by hiding the lower menu of the desktop Pipe recorder and control the recording process through the JS Control API using your own buttons.

Controlling the recorder through JavaScript when using the 2.0 embed code (HTML)

Example code: check out the example code we've prepared on how to control the Pipe recorder using the JS APIs when using the 2.0 embed code:

When using the 2.0 HTML version of the embed code, you do not have a reference in JS for each recorder so you need to obtain one first. The way to obtain these references is by using the PipeSDK.getRecorderById() method inside the PipeSDK.onRecordersInserted() method. Once the reference is obtained you can execute its record() method:

<input type="button" class="btn" value="Record" id="recordbtn" />
<script>
PipeSDK.onRecordersInserted = function(){
    myRecorder =  PipeSDK.getRecorderById('UNIQUE_ID_OF_THE_PIPERECORDER_TAG');
    myRecorder.onReadyToRecord = function(id, type){
    	document.getElementById("recordbtn").onclick = function (){
    		myRecorder.record();
    	}
    }
}
</script>

In the example above, after the recorder object/objects have been initialized, we get a reference (myRecorder) to our recorder using the PipeSDK.getRecorderById() method. After that, we assign a function to the click event of a button on our page. When this button is clicked, the recording is triggered using the myRecorder.record() method.

PipeSDK is the root object that is automatically initialized and accessible after pipe.js has loaded in the browser.

UNIQUE_ID_OF_THE_PIPERECORDER_TAG is the ID of any piperecorder tag on your page, that will automatically be replaced with a Pipe recorder. This id becomes the ID of the recorder.

All of the JavaScript Control API methods will be made available only after the onReadyToRecord() event has been triggered.

Full list of control methods you can use when using the 2.0 embed code

Here is the full list of methods that you can call on the Pipe recorder and that make up the JS Control API:

record();

Description: This call will trigger the recorder to record a video or audio stream or to rerecord one if one is already recorded (equivalent to pressing the RECORD button).

It should be executed only after:

  1. onCamAccess() is called by Pipe and the value of allowed is true
  2. onRecorderReady() (Embed Code v1.0) or onReadyToRecord() (Embed Code v2.0) is called by Pipe

If executed before the above 2 events the initial recording screen OR mic/cam device selection screens might be shown by Pipe in which case calling record() will do nothing.

Return Value: none

stopVideo();

Description: This call will trigger the Pipe desktop client to stop the recording of a stream if a recording is in progress.

Return Value: none

playVideo();

Description: This call will play the recorded stream if there is one. This will not be available during or before the recording process (equivalent to pressing the PLAY button).

Return Value: none

pause();

Description: This call will pause the video recording that is currently playing. The call will not do anything if a video recording is not playing. The call is equivalent to pressing the PAUSE PLAYBACK button. Calling the function a second time will not do anything, you need to call playVideo() to resume playback.

Return Value: none

save();

Description: If recording autosaving is disabled you can use this call to save the desired recording (equivalent to pressing the Save button). This will not be available if recording autosaving is enabled.

Return Value: none

getStreamTime();

Description: This will return the time of the played or the currently recording stream.

Return Value: a number with the current length of the recording (if called during the recording process)

getPlaybackTime();

Description: This will return the current time of the recording during playback.

Return Value: a number with the current time of the recording if called during the playback process or 0 if called outside of playback

getStreamName();

Description: This call will return the stream name at any time but beware that the name of the stream might not be set at the time of the call. This is useful when you need the name of the stream during the recording.

Return Value: a string, the stream name WITHOUT the file extension. If the stream name is not set it will return an empty string.

remove();

Description: Executing this method will remove the Pipe recorder HTML elements from the page and releases any in-use resources (webcam, connection to media server) without removing the div or piperecorder element in which it was first inserted. After removal, the div or piperecorder element can be reused to re-insert the recorder in the page. It works for all the Pipe clients.

Return Value: none

Controlling the recorder through JavaScript when using the 2.0 embed code (JS)

Example code: check out the example code we've prepared on how to control the Pipe recorder using the JS APIs when using the 2.0 embed code:

When using the 2.0 JavaScript version of the embed code, the recorder object is received in the callback function of the PipeSDK.insert() method. Once the recorder object is received you can call its record() method like this:

<input type="button" class="btn" value="Record" id="recordbtn" />
<script>
PipeSDK.insert('UNIQUE_ID_OF_THE_REPLACED_DIV', paramsObject, function(myRecorderObject){
    myRecorderObject.onReadyToRecord = function(id, type){
        document.getElementById("recordbtn").onclick = function (){
            myRecorderObject.record();
        }
    }
}
</script>

In the example above, in the callback function of PipeSDK.insert() we get our recorder object named in this case myRecorderObject. After that, we assign a function to the click event of a button on our page. When this button is clicked, the recording is triggered using myRecorderObject.record().

PipeSDK is the root object that is automatically initialized and accessible after pipe.js has loaded in the browser.

UNIQUE_ID_OF_THE_REPLACED_DIV is the ID of any div element in your page, that will automatically be replaced with a Pipe recorder once the PipeSDK.insert() method is called. This id will become the ID of the recorder.

All of the JavaScript Control API methods will be made available only after the onReadyToRecord() event has been triggered.

Full list of control methods you can use when using the 2.0 embed code

Here is the full list of methods that you can call on the Pipe recorder and that make up the JS Control API:

record();

Description: This call will trigger the recorder to record a video or audio stream or to rerecord one if one is already recorded (equivalent to pressing the RECORD button).

It should be executed only after:

  1. onCamAccess() is called by Pipe and the value of allowed is true
  2. onRecorderReady() (Embed Code v1.0) or onReadyToRecord() (Embed Code v2.0) is called by Pipe

If executed before the above 2 events the initial recording screen OR mic/cam device selection screens might be shown by Pipe in which case calling record() will do nothing.

Return Value: none

stopVideo();

Description: This call will trigger the Pipe desktop client to stop the recording of a stream if a recording is in progress.

Return Value: none

playVideo();

Description: This call will play the recorded stream if there is one. This will not be available during or before the recording process (equivalent to pressing the PLAY button).

Return Value: none

pause();

Description: This call will pause the video recording that is currently playing. The call will not do anything if a video recording is not playing. The call is equivalent to pressing the PAUSE PLAYBACK button. Calling the function a second time will not do anything, you need to call playVideo() to resume playback.

Return Value: none

save();

Description: If recording autosaving is disabled you can use this call to save the desired recording (equivalent to pressing the Save button). This will not be available if recording autosaving is enabled.

Return Value: none

getStreamTime();

Description: This will return the time of the played or the currently recording stream.

Return Value: a number with the current length of the recording (if called during the recording process)

getPlaybackTime();

Description: This will return the current time of the recording during playback.

Return Value: a number with the current time of the recording if called during the playback process or 0 if called outside of playback

getStreamName();

Description: This call will return the stream name at any time but beware that the name of the stream might not be set at the time of the call. This is useful when you need the name of the stream during the recording.

Return Value: a string, the stream name WITHOUT the file extension. If the stream name is not set it will return an empty string.

remove();

Description: Executing this method will remove the Pipe recorder HTML elements from the page and releases any in-use resources (webcam, connection to media server) without removing the div or piperecorder element in which it was first inserted. After removal, the div or piperecorder element can be reused to re-insert the recorder in the page. It works for all the Pipe clients.

Return Value: none

JavaScript Events API v2.0 (Desktop recorder)

Every time something happens with the desktop recorder (a button is pressed, the connection succeeds, etc.), the recorder will execute a specific JavaScript function with information about the event.

You can add these event functions to your HTML/JS app and extend them with your own code to do your bidding.

Here are some of the things you can do by extending these event functions:

  • show a message, a warning, or a timer on the HTML page
  • activate an input field or button in the HTML page
  • redirect the user to another page
Example code: check out the example code we've prepared on how to listen for JS events when using the 2.0 embed code:

Since the v2 embed code supports multiple recorders on the same page, to override a recorde's event functions, we need to get the desired recorder object 1st. Here's how to do it:

Getting the recorder object

This is done differently depending on what version of the embed code you are using.

When using the v2.0 HTML embed code you can use the PipeSDK.getRecorderById() method to get a reference to a recorder object.

For example:

var myRecorderObject = PipeSDK.getRecorderById('UNIQUE_ID_OF_THE_RECORDER');

will get a reference to the recorder which replaced the <piperecorder...> HTML element with the id UNIQUE_ID_OF_THE_RECORDER.

To make sure all the recorder object/objects have been initialized and they're ready you must execute PipeSDK.getRecorderById() only once the PipeSDK.onRecordersInserted() method fires.

Example: add this JS code to your HTML page to be notified when the user clicks the record button in the desktop recorder:

<script>
PipeSDK.onRecordersInserted = function(){
    myRecorderObject =  PipeSDK.getRecorderById('UNIQUE_ID_OF_THE_RECORDER');
    myRecorderObject.btRecordPressed = function(id){
        var args = Array.prototype.slice.call(arguments);
        console.log("btRecordPressed("+args.join(', ')+")");
    }
}
</script>

When using the v2.0 JavaScript version of the embed code, the recorder object is retrieved in the callback function of the PipeSDK.insert() method. This is where you can override the event functions.

Example: add this JS code to your app to insert a recorder in the app and get notified via console.log when the user clicks the record button in the desktop recorder:

PipeSDK.insert('UNIQUE_ID_OF_THE_RECORDER', paramsObject, function(myRecorderObject){
    myRecorderObject.btRecordPressed = function(id){
        var args = Array.prototype.slice.call(arguments);
        console.log("btRecordPressed("+args.join(', ')+")");
    }
});

The desktop recorder has 2 different sets of events and corresponding event functions:

Event functions executed when recording a new video using the desktop recorder with embed code 2.0

Below is the list of JavaScript event functions that the Pipe client will trigger when the user is recording a video from a desktop device.

onReadyToRecord(recorderId, recorderType)

Description: The Pipe recorder is ready to start recording videos(the interface has been built, you have allowed access to your webcam and you can see yourself in the browser)

Passed Parameters:
  • recorderId - the recorder id
  • recorderType - a string representing the type of the recorder (flash or HTML5).

Example:

myRecorderObject.onReadyToRecord = function(recorderId, recorderType) {
    var args = Array.prototype.slice.call(arguments);
    console.log("onReadyToRecord("+args.join(', ')+")");
});
userHasCamMic(recorderId,cam_number,mic_number)

Description: Pipe detects the number of cams and mics a user has

Passed Parameters:
  • recorderId - the recorder id
  • cam_number - number of webcam drivers the user has installed on his computer
  • mic_number - number of sound cards and sound sources the user has installed on his computer

Example:

myRecorderObject.userHasCamMic = function(recorderId,cam_number,mic_number){
	var args = Array.prototype.slice.call(arguments);
	console.log("userHasCamMic("+args.join(', ')+")");
}
btRecordPressed(recorderId)

Description: RECORD button is pressed. This event does NOT mark the exact start of the recording. See onRecordingStarted.

Passed Parameters:
  • recorderId - the recorder id

Example:

myRecorderObject.btRecordPressed = function(recorderId){
	var args = Array.prototype.slice.call(arguments);
	console.log("btRecordPressed("+args.join(', ')+")");
}
btStopRecordingPressed(recorderId)

Description: STOP RECORD button is pressed

Passed Parameters:
  • recorderId - the recorder id

Example:

myRecorderObject.btStopRecordingPressed = function(recorderId){
	var args = Array.prototype.slice.call(arguments);
	console.log("btStopRecordingPressed("+args.join(', ')+")");
}
btPlayPressed(recorderId)

Description: PLAY button is pressed

Passed Parameters:
  • recorderId - the recorder id

Example:

myRecorderObject.btPlayPressed = function(recorderId){
	var args = Array.prototype.slice.call(arguments);
	console.log("btPlayPressed("+args.join(', ')+")");
}
btPausePressed(recorderId)

Description: PAUSE button is pressed

Passed Parameters:
  • recorderId - the recorder id

Example:

myRecorderObject.btPausePressed = function(recorderId){
	var args = Array.prototype.slice.call(arguments);
	console.log("btPausePressed("+args.join(', ')+")");
}
onUploadDone(recorderId, streamName, streamDuration, audioCodec, videoCodec, fileType, audioOnly, location)

Description: Recorded data finishes streaming/uploading to the media server

Passed Parameters:
  • recorderId - the recorder id
  • streamName - a string representing the name of the stream WITHOUT the file extension
  • streamDuration - the duration of the recording/audio file in seconds but accurate to the millisecond (like this: 4.322)
  • audioCodec - the audio codec used for the recording
  • videoCodec - the video codec used for the recording
  • fileType - the initial file extension for the recording
  • audioOnly - true if it is an audio-only recording, false otherwise
  • location - will take one of 4 values:
    1. eu2-addpipe.s3.nl-ams.scw.cloud
    2. us1-addpipe.s3-us-west-1.amazonaws.com
    3. us2-addpipe.s3.us-east-1.amazonaws.com
    4. eu1-addpipe.s3.eu-central-1.amazonaws.com

The location contains the subdomain of the regional storage bucket where the final recording and snapshot will be stored by Pipe. For the EU2 region, for example, the recording will be stored at https://eu2-addpipe.s3.nl-ams.scw.cloud/ACCOUNT_HASH/FILE_NAME.mp4. The snapshot will be at the same location but with the .jpg extension. If Do Not Store Files is turned on, the final processed files will not be pushed to this storage location.

Example:

myRecorderObject.onUploadDone = function(recorderId, streamName, streamDuration, audioCodec, videoCodec, fileType, audioOnly, location){
	var args = Array.prototype.slice.call(arguments);
	console.log("onUploadDone("+args.join(', ')+")");
}
onUploadProgress(recorderId, percent)

Description: Triggers every time upload progress is made.

Passed Parameters:
  • recorderId - the recorder id
  • percent - the percent at which the upload progress is at

Example:

myRecorderObject.onUploadProgress = function(recorderId, percent){
	var args = Array.prototype.slice.call(arguments);
	console.log("onUploadProgress("+args.join(', ')+")");
}
onCamAccess(recorderId, allowed)

Description:

onCamAccess is triggered:

  1. for macOS, after clicking the Allow or Don't Allow buttons in the operating system's privacy dialog box asking permission for camera and microphone access. The dialog box will be brought up by the operating system the first time the recorder tries to access the camera or the microphone.
  2. after clicking the Allow or Block (Don't Allow for Safari) buttons in the browser's privacy dialog box asking permission for camera and microphone access. The dialog box will be brought up by the browser the first time the recorder tries to access the camera or the microphone.
  3. automatically if a previous choice (Allow or Block) made by the user is persistent.

Here is an example of Chrome's privacy dialog box:

Chrome permission dialog box to allow Pipe to use camera/microphone

And here is how macOS warns the user for an additional time that Firefox would like to access the camera and microphone.

macOS permission dialog box to allow Pipe to use camera/microphone
  • recorderId - the recorder id
  • allowed
    • is true if:
      • the user clicked the [Allow] button OR
      • clicked [Allow] in a previous session and that choice is persistent (in which case no additional dialog box is shown).
    • and false if
      • the user clicked the [Block] button OR
      • clicked [Block] in a previous session and that choice is persistent (in which case no additional dialog box is shown).

Example:

myRecorderObject.onCamAccess = function(recorderId, allowed){
	var args = Array.prototype.slice.call(arguments);
	console.log("onCamAccess("+args.join(', ')+")");
}
onPlaybackComplete(recorderId)

Description: Pipe finishes playback of recording stream

Passed Parameters:
  • recorderId - the recorder id

Example:

myRecorderObject.onPlaybackComplete = function(recorderId){
	var args = Array.prototype.slice.call(arguments);
	console.log("onPlaybackComplete("+args.join(', ')+")");
}
onRecordingStarted(recorderId)

Description: the Pipe desktop client started recording. This event is called ONLY if there's data to record and, when it is called, it will be called with a 200-220ms delay after the record button is pressed and the btRecordPressed event is triggered.

Parameters:
  • recorderId - the recorder id

Example:

myRecorderObject.onRecordingStarted = function(recorderId){
	var args = Array.prototype.slice.call(arguments);
	console.log("onRecordingStarted("+args.join(', ')+")");
}
onConnectionClosed(recorderId)

Description: the connection to the media server has failed completely (the connection could not be reestablished even after the 30 reconnection attempts in the HTML5 recorder)

Parameters:
  • recorderId - the recorder id

Example:

myRecorderObject.onConnectionClosed = function(recorderId){
	var args = Array.prototype.slice.call(arguments);
	console.log("onConnectionClosed("+args.join(', ')+")");
}
onConnectionStatus(recorderId, status)

Description: Called by the Pipe desktop client for every connection event

Passed Parameters:
  • status - the actual connection status:

HTML5 only

• connected
- The connection is live
• disconnected: ping timeout
- When the client loses Internet connection.
• disconnected: io server disconnect
- When the media server is shut down.
• disconnected: io client disconnect
- The socket was manually disconnected.
• disconnected: transport close
- The connection was closed (for example the user has lost connection, or the network was changed from WiFi to 4G).
• disconnected: transport error
- The connection has encountered an error (for example the server was killed during an HTTP long-polling cycle)
• error
- Thrown when an error occurs.
• connect_timeout
- Thrown when connection timeouts.
• reconnecting
- Actively trying to reconnect. A total of 30 reconnect attempts will be made, each attempt at an interval between 1000 ms - 5000 ms.
• connect_error: (+ error)
- Thrown after a connection error.
• reconnect_error
- Thrown only after a reconnecting attempt when the reconnection attempt resulted in an error.
• reconnect_failed
- Reconnection has completely failed after a total number of 30 attempts were made. The client is considered fully disconnected.
• reconnected
- Thrown only after a reconnecting attempt when the reconnection was successful.

recorderId - the value of recorderId property of flashvars object in the embed code

Example:

myRecorderObject.onConnectionStatus = function(recorderId, status){
	var args = Array.prototype.slice.call(arguments);
	console.log("onConnectionStatus("+args.join(', ')+")");
}
onMicActivityLevel(recorderId, currentActivityLevel)

Description: the function is called by the Pipe desktop client every 10th of a second (100 milliseconds) with the current microphone level.

Passed Parameters:
  • recorderId - the recorder id
  • currentActivityLevel - The amount of sound the microphone is detecting in numeric values between 0 and 100. From our experience, it's hard to get values over 50 though.

Example:

myRecorderObject.onMicActivityLevel = function(recorderId, currentActivityLevel){
	var args = Array.prototype.slice.call(arguments);
	console.log("onMicActivityLevel("+args.join(', ')+")");
}
onSaveOk(recorderId, streamName, streamDuration, cameraName, micName, audioCodec, videoCodec, filetype, videoId, audioOnly, location)

Description: The recording data has been fully streamed to our media servers for further processing.

This is not a confirmation that the recorded file is ready for delivery as the recording will take a few seconds to be processed (conversion to .mp4, snapshot extraction, rotation, push to storage location).

Use Pipe's webhooks to be notified when the recording is fully processed and ready for delivery.

Passed Parameters:
  • recorderId - the recorder id
  • streamName - a string representing the name of the stream WITHOUT the file extension
  • streamDuration - the duration of the recorded video/audio file in seconds but accurate to milliseconds (like this: 4.322)
  • cameraName - the name of the webcam driver the user has selected for capturing video data
  • micName - the name of the sound card/mic driver that has been selected for capturing audio data
  • audioCodec - the audio codec used for the recording
  • videoCodec - the video codec used for the recording
  • fileType - the initial file extension for the recording
  • videoId - a string representing the recording id in Pipe's database, same recording id that is sent through the webhook)
  • audioOnly - true if it is an audio-only recording, false otherwise
  • location - will take one of 4 values:
    1. eu2-addpipe.s3.nl-ams.scw.cloud
    2. us1-addpipe.s3-us-west-1.amazonaws.com
    3. us2-addpipe.s3.us-east-1.amazonaws.com
    4. eu1-addpipe.s3.eu-central-1.amazonaws.com

The location contains the subdomain of the regional storage bucket where the final recording and snapshot will be stored by Pipe. For the EU2 region, for example, the recording will be stored at https://eu2-addpipe.s3.nl-ams.scw.cloud/ACCOUNT_HASH/FILE_NAME.mp4. The snapshot will be at the same location but with the .jpg extension. If Do Not Store Files is turned on, the final processed files will not be pushed to this storage location.

Example:

myRecorderObject.onSaveOk = function(recorderId, streamName, streamDuration, cameraName, micName, audioCodec, videoCodec, fileType, videoId, audioOnly, location){
	var args = Array.prototype.slice.call(arguments);
	console.log("onSaveOk("+args.join(', ')+")");
}

Event functions executed when uploading an existing file using the desktop recorder with embed code 2.0

Below is the list of JavaScript events that the Pipe client will dispatch when the user is UPLOADING an existing video using the desktop recorder. You can turn on the feature from your embed code or from the Pipe account dashboard when generating an embed code, details here.

onDesktopVideoUploadStarted(recorderId, filename, filetype, audioOnly)

Description: The user has selected a recording from the computer's library and the recording is ready for upload.

Passed Parameters:
  • recorderId - the recorder id
  • filename - the (automatically generated) name of the recording without extension
  • filetype - the file extension of the uploaded video, it can be mp4, mov, 3gp, m4v, mkv, avi, mp3, wma, etc.
  • audioOnly - true if we're expecting an audio-only recording, false otherwise

Example:

myRecorderObject.onDesktopVideoUploadStarted = function(recorderId, filename,filetype, audioOnly){
	var args = Array.prototype.slice.call(arguments);
	console.log("onDesktopVideoUploadStarted("+args.join(', ')+")");
onDesktopVideoUploadSuccess(recorderId, filename, filetype, videoId, audioOnly, location)

Description: The recording has finished uploading successfully.

Passed Parameters:
  • recorderId - the recorder id
  • filename - the automatically generated name of the recording
  • filetype - the file extension of the uploaded video, it can be mp4, mov, 3gp, m4v, mkv, avi, mp3, wma, etc.
  • videoId - a string representing the recording id in Pipe's database (same recording id that is sent through the webhook)
  • audioOnly - true if it is an audio-only recording, false otherwise
  • location - will take one of 4 values:
    1. eu2-addpipe.s3.nl-ams.scw.cloud
    2. us1-addpipe.s3-us-west-1.amazonaws.com
    3. us2-addpipe.s3.us-east-1.amazonaws.com
    4. eu1-addpipe.s3.eu-central-1.amazonaws.com

The location contains the subdomain of the regional storage bucket where the final recording and snapshot will be stored by Pipe. For the EU2 region, for example, the recording will be stored at https://eu2-addpipe.s3.nl-ams.scw.cloud/ACCOUNT_HASH/FILE_NAME.mp4. The snapshot will be at the same location but with the .jpg extension. If Do Not Store Files is turned on, the final processed files will not be pushed to this storage location.

Example:

myRecorderObject.onDesktopVideoUploadSuccess = function(recorderId, filename,filetype,videoId,audioOnly,location){
	var args = Array.prototype.slice.call(arguments);
	console.log("onDesktopVideoUploadSuccess("+args.join(', ')+")");
}
onDesktopVideoUploadProgress(recorderId, percent)

Description: Triggers every time upload progress is made.

Parameters:
  • recorderId - the recorder id
  • percent - the percent at which the upload progress is at

Example:

myRecorderObject.onDesktopVideoUploadProgress = function(recorderId, percent){
	var args = Array.prototype.slice.call(arguments);
	console.log("onDesktopVideoUploadProgress("+args.join(', ')+")");
}
onDesktopVideoUploadFailed(recorderId, error)

Description: There was an error while uploading the recording.

Passed Parameters:
  • recorderId - the recorder id
  • error - The error thrown when the upload failed to complete

Example:

myRecorderObject.onDesktopVideoUploadFailed = function(recorderId, error){
	var args = Array.prototype.slice.call(arguments);
	console.log("onDesktopVideoUploadFailed("+args.join(', ')+")");
}

JavaScript Events API v2.0 (Mobile native recorder)

Every time something happens with the mobile native recorder (a video is starting to upload, an upload has finished, etc.) the recorder will execute a specific JavaScript function with information about the event.

You can add these mobile native recorder event functions to your HTML/JS app and extend them with your own code to do your bidding.

Since the v2.0 embed code supports multiple recorders on the same page, to override a recorder's event functions, we need to get the desired recorder object 1st.

Getting the recorder object

This is done differently depending on what version of the embed code you are using.

When using the v2.0 HTML embed code you can use the PipeSDK.getRecorderById() method to get a reference to a recorder object.

For example:

var myRecorderObject = PipeSDK.getRecorderById('UNIQUE_ID_OF_THE_RECORDER');

will get a reference to the recorder which replaced the <piperecorder> HTML element with the id UNIQUE_ID_OF_THE_RECORDER.

To make sure all the recorder object/objects have been initialized and they're ready you must execute PipeSDK.getRecorderById() only once the PipeSDK.onRecordersInserted() method fires.

Example: add this JS code to your HTML page to be notified when the upload starts after the user has recorded a video in the iOS/Android-controlled UI:

<script>
PipeSDK.onRecordersInserted = function(){
    myRecorderObject =  PipeSDK.getRecorderById('UNIQUE_ID_OF_THE_RECORDER');
    myRecorderObject.onVideoUploadStarted = function(id, filename, filetype, audioOnly){
        var args = Array.prototype.slice.call(arguments);
        console.log("onVideoUploadStarted("+args.join(', ')+")");
    }
}
</script>

When using the v2.0 JavaScript version of the embed code, the recorder object is retrieved in the callback function of the PipeSDK.insert() method. This is where you can override the event functions.

Example: add this JS code to your app to insert a recorder in the app and get notified via console.log when the upload starts after the user has recorded a video in the iOS/Android-controlled UI:

PipeSDK.insert('UNIQUE_ID_OF_THE_RECORDER', paramsObject, function(myRecorderObject){
    myRecorderObject.onVideoUploadStarted = function(id, filename, filetype, audioOnly){
        var args = Array.prototype.slice.call(arguments);
        console.log("onVideoUploadStarted("+args.join(', ')+")");
    }
});

You can replace onVideoUploadStarted(id, filename, filetype, audioOnly) in the example above with any of the 2.0 mobile event functions below.

onVideoUploadStarted(recorderId, filename, filetype, audioOnly)

Description: The user has recorded a new video or chosen an existing video from the device's library and the video is ready for upload.

Passed Parameters:
  • recorderId - the recorder id
  • filename - the (automatically generated) name of the recording without extension
  • filetype - the file extension of the uploaded video, it can be mp4, mov, 3gp, m4v, etc.
  • audioOnly - true if we're expecting an audio-only recording, false otherwise

Example:

myRecorderObject.onVideoUploadStarted = function(recorderId, filename,filetype, audioOnly){
	var args = Array.prototype.slice.call(arguments);
	console.log("onVideoUploadStarted("+args.join(', ')+")");
}
onVideoUploadSuccess(recorderId, filename, filetype, videoId, audioOnly, location)

Description: The new or existing recording has finished uploading successfully.

Passed Parameters:
  • recorderId - the recorder id
  • filename - the automatically generated name of the recording
  • filetype - the file extension of the uploaded video, it can be mp4, mov, 3gp, m4v, etc.
  • videoId - a string representing the recording id in Pipe's database (same recording id that is sent through the webhook)
  • audioOnly - true if it is an audio-only recording, false otherwise
  • location - will take one of 4 values:
    1. eu2-addpipe.s3.nl-ams.scw.cloud
    2. us1-addpipe.s3-us-west-1.amazonaws.com
    3. us2-addpipe.s3.us-east-1.amazonaws.com
    4. eu1-addpipe.s3.eu-central-1.amazonaws.com

The location contains the subdomain of the regional storage bucket where the final recording and snapshot will be stored by Pipe. For the EU2 region, for example, the recording will be stored at https://eu2-addpipe.s3.nl-ams.scw.cloud/ACCOUNT_HASH/FILE_NAME.mp4. The snapshot will be at the same location but with the .jpg extension. If Do Not Store Files is turned on, the final processed files will not be pushed to this storage location.

Example:

myRecorderObject.onVideoUploadSuccess = function(recorderId, filename,filetype,videoId,audioOnly,location){
	var args = Array.prototype.slice.call(arguments);
	console.log("onVideoUploadSuccess("+args.join(', ')+")");
}
onVideoUploadProgress(recorderId, percent)

Description: Triggers every time upload progress is made.

Passed Parameters:
  • recorderId - the recorder id
  • percent - the percent at which the upload progress is at

Example:

myRecorderObject.onVideoUploadProgress = function(recorderId, percent){
	var args = Array.prototype.slice.call(arguments);
	console.log("onVideoUploadProgress("+args.join(', ')+")");
}
onVideoUploadFailed(recorderId, error)

Description:There was an error while uploading the recording.

Passed Parameters:
  • recorderId - the recorder id
  • error - The error thrown when the upload failed to complete

Example:

myRecorderObject.onVideoUploadFailed = function(recorderId, error){
	var args = Array.prototype.slice.call(arguments);
	console.log("onVideoUploadFailed("+args.join(', ')+")");
}

REST API v1


The REST API v1 allows you to request information from our servers programmatically and in a standardized manner, using JSON.

All requests are made against https://api.addpipe.com and are authenticated by sending the API key as the value of the X-PIPE-AUTH header.

API Key

The API Key can be found on your Pipe account details page. If you do not already have one, you can generate your initial API Key there.

The key should be kept in a safe place, like any other password. If compromised, the API Key can be regenerated and replaced with a new one.

API endpoints

/video

Perform actions for (undeleted) recordings from your account.

Parameters

  • all, optional. May be included or omitted from the request.

Requests available:
  • GET https://api.addpipe.com/video/all - get all videos from your account. JSON response
/video/:id

Perform actions for a specific recording.

Parameters:
  • :id - numerical representation of the recording ID which can be found in your Pipe account, under the Recordings tab.

Requests available:
  • GET https://api.addpipe.com/video/13360 - get specific information for this recording id JSON response
  • DELETE https://api.addpipe.com/video/167841 - deletes the recording with this id

Note: you can now also automatically delete recordings after a number of days through the new lifecycle feature available on the environment edit page.

/account

Perform actions on your account.


Requests available:
  • GET https://api.addpipe.com/account - get information regarding your Pipe account JSON response
/environment

Access environment-related information.


Requests available:
  • GET https://api.addpipe.com/environment - get information regarding your environment JSON response
/environment/:id

Get details about a specific environment.

Parameters:
  • :id - numerical representation of the environment's unique ID which can be retrieved by using the GET /environment endpoint

Requests available:
  • GET https://api.addpipe.com/environment/1537 - get specific information for this environment id JSON response
/webhook

Access webhook-related information.


Requests available:
  • GET https://api.addpipe.com/webhook - get information regarding your webhooks JSON response
/webhook/:id

Perform actions for a specific webhook.

Parameters:
  • :id - the numeric webhook id which can be retrieved by using the GET /webhook endpoint.

Requests available:
  • PUT https://api.addpipe.com/webhook/69 - update your webhook by following the below JSON scheme
For PUT requests, make sure you send at least one of the following:
{
  "webhook": "http://your-fancy-website.com/webhook",
  "webhook_recorded": "0",
  "webhook_transcoded": "1",
  "webhook_converted": "1",
  "webhook_copied_pipe_s3":"0",
  "webhook_copied_s3": "0",
  "webhook_copied_ftp": "0",
  "webhook_copied_dbox": "0",
  "active":"1"
}
                    
/amazon

Perform actions on your S3-related information stored with Pipe.


Requests available:
  • GET https://api.addpipe.com/amazon - retrieve basic information about your Amazon S3 credentials for every environment JSON response
/amazon/:id

Perform actions for a specific Amazon S3 credentials set.

Parameters:
  • :id - numerical representation of the Amazon S3 credentials ID which can be retrieved by using the GET /amazon endpoint.

Requests available:
  • PUT https://api.addpipe.com/amazon/69 - update your Amazon credentials following the below JSON scheme
For PUT requests, make sure you send at least one of the following:
{
  "amz_key": "YourAmazonKey",
  "amz_secret": "YourAmazonSecret",
  "amz_bucket": "TheBucket",
  "amz_bucket_region": "us-east-1"
}                   
/ftp

Perform actions on your FTP(S)/SFTP-related information stored with Pipe.


Requests available:
  • GET https://api.addpipe.com/ftp - get information regarding your SFTP or FTP(S) credentials JSON response
/ftp/:id

Perform actions for a specific set of SFTP or FTP(S) credentials.

Parameters:
  • :id - the numerical id for the set of SFTP or FTP(S) credentials. It can be retrieved by using the GET /ftp endpoint.

Requests available:
  • PUT https://api.addpipe.com/ftp/69 - update your SFTP or FTP(S) credentials following the below JSON scheme
For PUT requests, make sure you send at least one of the following:
{
  "ftp_host": "FtpHost",
  "ftp_port": "21",
  "ftp_username": "FtpUsername",
  "ftp_password": "FtpPass",
  "ftp_dir": "/dir/to/saved_videos",
  "sftp_host": "SftpHost",
  "sftp_port": "22",
  "sftp_username": "SftpUser",
  "sftp_password": "SftpPass"
  "sftp_dir": "/dir/to/saved_videos"
}                       
/dropbox

Perform actions on your Dropbox-related information stored with Pipe.


Requests available:
  • GET https://api.addpipe.com/dropbox - get some of the information around your Dropbox credentials JSON response
/dropbox/:id

Perform actions for a specific Dropbox credentials set.

Parameters:
  • :id - the numeric id for the Dropbox credentials set, it can be obtained through GET /dropbox endpoint.

Requests available:
  • PUT https://api.addpipe.com/dropbox/69 - update your Dropbox credentials following the below JSON scheme
For PUT requests, make sure you send at least one of the following:
{
  "dbox_token": "YourDropboxAccessToken",
  "dbox_folder": "YourDropboxFolder"
}                    

Code Examples

GET the info on a particular recording

$headers = array(
    'x-pipe-auth: YOUR-PIPE-API-KEY'
);

// init curl object
$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.addpipe.com/video/YOUR-RECORDING-ID');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

// Timeout in seconds
curl_setopt($ch, CURLOPT_TIMEOUT, 30);

$content = curl_exec($ch);

echo $content;

DELETE a recording

$headers = array(
    'x-pipe-auth: YOUR-PIPE-API-KEY'
);

// init curl object
$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.addpipe.com/video/YOUR-RECORDING-ID');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

// Timeout in seconds
curl_setopt($ch, CURLOPT_TIMEOUT, 30);

$content = curl_exec($ch);

echo $content;

PUT a new set of Amazon S3 credentials


var data = JSON.stringify({
  "amz_key": "",
  "amz_secret": "",
  "amz_bucket": "",
  "amz_bucket_region": ""
});

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

xhr.addEventListener("readystatechange", function () {
  if (this.readyState === this.DONE) {
    console.log(this.responseText);
  }
});

xhr.open("PUT", "https://api.addpipe.com/amazon");
xhr.setRequestHeader("x-pipe-auth", "Your-Pipe-Api-Key");
xhr.setRequestHeader("content-type", "application/json");
xhr.setRequestHeader("cache-control", "no-cache");

xhr.send(data);

PUT a new set of Amazon S3 credentials


package main

import (
	"fmt"
	"strings"
	"net/http"
	"io/ioutil"
)

func main() {

	url := "https://api.addpipe.com/amazon"

	payload := strings.NewReader("{\n  \"amz_key\": \"\",\n  \"amz_secret\": \"\",\n  \"amz_bucket\": \"\",\n  \"amz_bucket_region\": \"\"\n}")

	req, _ := http.NewRequest("PUT", url, payload)

	req.Header.Add("x-pipe-auth", "Your-Pipe-Api-Key")
	req.Header.Add("content-type", "application/json")
	req.Header.Add("cache-control", "no-cache")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := ioutil.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}

PUT a new set of Amazon S3 credentials


import http.client

conn = http.client.HTTPConnection("api.addpipe.com")

payload = "{\n  \"amz_key\": \"\",\n  \"amz_secret\": \"\",\n  \"amz_bucket\": \"\",\n  \"amz_bucket_region\": \"\"\n}"

headers = {
    'x-pipe-auth': "Your-Pipe-Api-Key",
    'content-type': "application/json",
    'cache-control': "no-cache"
    }

conn.request("PUT", "/account", payload, headers)

res = conn.getresponse()
data = res.read()

print(data.decode("utf-8"))

PUT a new set of Amazon S3 credentials


require 'uri'
require 'net/http'

url = URI("https://api.addpipe.com/amazon")

http = Net::HTTP.new(url.host, url.port)

request = Net::HTTP::Put.new(url)
request["x-pipe-auth"] = 'Your-Pipe-Api-Key'
request["content-type"] = 'application/json'
request["cache-control"] = 'no-cache'
request.body = "{\n  \"amz_key\": \"\",\n  \"amz_secret\": \"\",\n  \"amz_bucket\": \"\",\n  \"amz_bucket_region\": \"\"\n}"

response = http.request(request)
puts response.read_body

PUT a new set of Amazon S3 credentials


wget --quiet \
  --method PUT \
  --header 'x-pipe-auth: Your-Pipe-Api-Key' \
  --header 'content-type: application/json' \
  --header 'cache-control: no-cache' \
  --body-data '{\n  "amz_key": "",\n  "amz_secret": "",\n  "amz_bucket": "",\n  "amz_bucket_region": ""\n}' \
  --output-document \
  - https://api.addpipe.com/amazon

PUT a new set of Amazon S3 credentials


curl --request PUT \
  --url https://api.addpipe.com/amazon \
  --header 'cache-control: no-cache' \
  --header 'content-type: application/json' \
  --header 'x-pipe-auth: Your-Pipe-Api-Key' \
  --data '{\n  "amz_key": "",\n  "amz_secret": "",\n  "amz_bucket": "",\n  "amz_bucket_region": ""\n}'