-
Notifications
You must be signed in to change notification settings - Fork 240
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #93 from bitmovin/develop
- Loading branch information
Showing
23 changed files
with
3,310 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
# AV1 break-even point calculation | ||
|
||
Calculate the break-even point of AV1 versus H.264 and H.265 encoding for our customers. | ||
|
||
### Tags | ||
|
||
- av1 | ||
- break-even point | ||
- encoding | ||
- h264 | ||
- h265 |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
<p class="text-justify"> | ||
The purpose of this calculator is to estimate the AV1 break-even point: | ||
How many views does it take to make up for the cost of using AV1 in addition to H.264 or H.265? | ||
</p> | ||
|
||
<h2 class="my-3">Inputs</h2> | ||
<p class="text-justify"> | ||
Note: These are price point values that may be different for different customers. | ||
</p> | ||
<form class="container" id="av1-form"> | ||
<div class="row"> | ||
|
||
<div class="col-md-6"> | ||
<div class="form-group"> | ||
<label for="encodingCostPerMinute">Encoding cost per billing minute</label> | ||
<div class="input-group"> | ||
<input class="form-control" | ||
id="encodingCostPerMinute" | ||
min="0" | ||
step="0.01" | ||
type="number" | ||
value="0.02" /> | ||
<div class="input-group-append"><span class="input-group-text">$</span></div> | ||
</div> | ||
<div> | ||
<small>Based on <a href="https://bitmovin.com/pricing/" rel="noreferrer" target="_blank" style="font-size: inherit;">Bitmovin flexible pricing</a></small> | ||
</div> | ||
</div> | ||
<div class="form-group"> | ||
<label for="ingressCostPerGb">Ingress cost per GB</label> | ||
<div class="input-group"> | ||
<input class="form-control" | ||
id="ingressCostPerGb" | ||
min="0" | ||
step="0.01" | ||
type="number" | ||
value="0" /> | ||
<div class="input-group-append"><span class="input-group-text">$</span></div> | ||
</div> | ||
<div> | ||
<small> </small> | ||
</div> | ||
</div> | ||
<div class="form-group"> | ||
<label for="egressCostPerGb">CDN delivery cost per GB</label> | ||
<div class="input-group"> | ||
<input class="form-control" | ||
id="egressCostPerGb" | ||
min="0" | ||
step="0.01" | ||
type="number" | ||
value="0.04" /> | ||
<div class="input-group-append"><span class="input-group-text">$</span></div> | ||
</div> | ||
</div> | ||
</div> | ||
|
||
<div class="col-md-6"> | ||
|
||
<div class="form-group"> | ||
<label for="numberOfStreamsUhd">Number of 4K UHD streams (H.264 bitrate: 12 Mbps; billing multiplier: 4)</label> | ||
<input class="form-control" | ||
id="numberOfStreamsUhd" | ||
min="1" | ||
step="1" | ||
type="number" | ||
value="2" /> | ||
<div> | ||
<small> </small> | ||
</div> | ||
</div> | ||
<div class="form-group"> | ||
<label for="numberOfStreamsHd">Number of HD streams (H.264 bitrate: 5 Mbps; billing multiplier: 2)</label> | ||
<input class="form-control" | ||
id="numberOfStreamsHd" | ||
min="1" | ||
step="1" | ||
type="number" | ||
value="3" /> | ||
<div> | ||
<small> </small> | ||
</div> | ||
</div> | ||
<div class="form-group"> | ||
<label for="numberOfStreamsSd">Number of SD streams (H.264 bitrate: 1 Mbps; billing multiplier: 1)</label> | ||
<input class="form-control" | ||
id="numberOfStreamsSd" | ||
min="1" | ||
step="1" | ||
type="number" | ||
value="4" /> | ||
</div> | ||
</div> | ||
|
||
</div> | ||
</form> | ||
|
||
<div class="container"> | ||
<div class="row"> | ||
|
||
<div class="col-md-6 my-2"> | ||
<div>Break-even point - H.264+AV1 vs H.264 only</div> | ||
<div id="av1-h264" class="font-weight-bold" style="font-size: 24px">--</div> | ||
</div> | ||
|
||
<div class="col-md-6 my-2"> | ||
<div>Break-even point - H.265+AV1 vs H.265 only</div> | ||
<div id="av1-h265" class="font-weight-bold" style="font-size: 24px">--</div> | ||
</div> | ||
|
||
</div> | ||
</div> | ||
<p class="text-justify"> | ||
Starting from these numbers of views, supplementing encodings with AV1 becomes more | ||
profitable than the respective codec alone. | ||
</p> | ||
|
||
<h3 class="my-3">Calculation multipliers</h3> | ||
<p class="text-justify"> | ||
These are the multipliers and factors used in our calculation<a href="#formulaFootnote" target="_self" class="btm-footnote"></a>. | ||
They are based on the | ||
<a href="https://bitmovin.com/_emcm/" rel="noreferrer" target="_blank" style="font-size: inherit;">Bitmovin Encoding Minute Calculation Methodology</a>. | ||
</p> | ||
<div class="container"> | ||
<div class="row"> | ||
<table class="table"> | ||
<thead> | ||
<th colspan="3">Multipliers</th> | ||
</thead> | ||
<tbody> | ||
<tr class="table-secondary"> | ||
<th class="col-3">SD</th> | ||
<th class="col-3">HD</th> | ||
<th class="col-3">UHD</th> | ||
</tr> | ||
<tr> | ||
<td>1</td> | ||
<td>2</td> | ||
<td>4</td> | ||
</tr> | ||
<tr class="table-secondary"> | ||
<th>PerTitle</th> | ||
<th colspan="2">3-Pass</th> | ||
</tr> | ||
<tr> | ||
<td>1.1</td> | ||
<td colspan="2">2</td> | ||
</tr> | ||
<tr class="table-secondary"> | ||
<th>H264</th> | ||
<th>H265</th> | ||
<th>AV1</th> | ||
</tr> | ||
<tr> | ||
<td>1</td> | ||
<td>2</td> | ||
<td>10</td> | ||
</tr> | ||
<tr class="table-secondary"> | ||
<th>AV1 efficiency improvement over H.264</th> | ||
<th colspan="2">AV1 efficiency improvement over H.265</th> | ||
</tr> | ||
<tr> | ||
<td>50%</td> | ||
<td colspan="2">30%</td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
</div> | ||
</div> | ||
|
||
<div class="mt-5"> | ||
<ol> | ||
<li class="mb-2" style="font-size: 12px;"> | ||
<span id="formulaFootnote"> | ||
Simplified formula break-even point: ( [AV1 encoding cost] + [AV1 ingress cost] ) / ( [H.26X CDN delivery cost] - [AV1 CDN delivery cost] ) | ||
</span> | ||
</li> | ||
</ol> | ||
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
{ | ||
"title": "AV1 break-even calculator", | ||
"description": "Calculate the break-even point for AV1 encoding", | ||
"long_description": "Calculate the break-even point for AV1 encoding, supplementing the existing encoding.", | ||
"executable": { | ||
"executable": true, | ||
"indexfile": "index.html" | ||
}, | ||
"tags": [ | ||
"AV1", | ||
"break-even", | ||
"calculator", | ||
"encoding", | ||
"return on investment" | ||
], | ||
"metadata":{ | ||
"title":"AV1 break-even point calculator » Demo | Bitmovin", | ||
"description": "Calculate the break-even point for AV1 encoding" | ||
}, | ||
"hide_github_link": true | ||
} |
157 changes: 157 additions & 0 deletions
157
encoding/av1-break-even-calculator/js/av1-break-even-calculator.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
$(function() { | ||
|
||
/** | ||
* https://stackoverflow.com/questions/17369098/simplest-way-of-getting-the-number-of-decimals-in-a-number-in-javascript | ||
* @param n {number} | ||
* @returns {number} | ||
*/ | ||
function countDecimals(n) { | ||
if (Math.floor(n.valueOf()) === n.valueOf()) { | ||
return 0; | ||
} | ||
return n.toString().split('.')[1].length || 0; | ||
} | ||
|
||
/** | ||
* https://stackoverflow.com/questions/11832914/round-to-at-most-2-decimal-places-only-if-necessary | ||
* @param n {number} | ||
* @param decimalPlaces {number} | ||
* @returns {number} | ||
*/ | ||
function roundDecimals(n, decimalPlaces) { | ||
var diff = Math.pow(10, decimalPlaces); | ||
return Math.round((n.valueOf() + Number.EPSILON) * diff) / diff; | ||
} | ||
|
||
/** | ||
* Generic handler that sets 'is-invalid' class to input, if value is NaN or <0 | ||
* @param onInputValue | ||
* @returns {function(value : Number | NaN): (undefined)} | ||
*/ | ||
function inputEventHandler(onInputValue) { | ||
return function(e) { | ||
var inputEl = $(this); | ||
inputEl.removeClass('is-invalid'); | ||
|
||
var numberValue = Number(e.target.value); | ||
if (isNaN(numberValue) || numberValue < 0 | ||
|| countDecimals(numberValue) > 2) { | ||
inputEl.addClass('is-invalid'); | ||
onInputValue(NaN); | ||
return; | ||
} | ||
|
||
onInputValue(numberValue); | ||
}; | ||
} | ||
|
||
(function initAv1Form() { | ||
// form values for calculation | ||
let encodingCostPerMinute = 0.02; | ||
let ingressCostPerGb = 0.00; | ||
let egressCostPerGb = 0.04; | ||
|
||
let numberOfStreamsUhd = 2; | ||
let numberOfStreamsHd = 3; | ||
let numberOfStreamsSd = 4; | ||
|
||
const multiplierStreamUhd = 4; | ||
const multiplierStreamHd = 2; | ||
const multiplierStreamSd = 1; | ||
const multiplierTechPerTitle = 1.1; | ||
const multiplierTech3Pass = 2; // Multipass | ||
const multiplierCodecAv1 = 10; | ||
|
||
// streams/renditions | ||
const improvementsAv1H264 = 0.5; // 50% | ||
const improvementsAv1H265 = 0.7; // 30% | ||
const mbpsH264Uhd = 12; | ||
const mbpsH264Hd = 5; | ||
const mbpsH264Sd = 1; | ||
const uhdRenditionGbPerMinH264 = (mbpsH264Uhd * 60) / (8 * 1000); // GB (8 * 1000) per min (60 seconds) | ||
const uhdRenditionGbPerMinH265 = uhdRenditionGbPerMinH264 * improvementsAv1H264 / improvementsAv1H265; | ||
const uhdRenditionGbPerMinAv1 = uhdRenditionGbPerMinH264 * improvementsAv1H264; | ||
|
||
// result elements | ||
const av1H264Element = $('#av1-h264'); | ||
const av1H265Element = $('#av1-h265'); | ||
|
||
const calculateBreakEvenPoints = () => { | ||
// stream composition | ||
const multiplierStreamComposition = numberOfStreamsUhd * multiplierStreamUhd | ||
+ numberOfStreamsHd * multiplierStreamHd | ||
+ numberOfStreamsSd * multiplierStreamSd; | ||
const encodingCostPerMinuteAv1 = encodingCostPerMinute * multiplierStreamComposition | ||
* multiplierTech3Pass * multiplierTechPerTitle * multiplierCodecAv1; | ||
|
||
// all stream bandwidth | ||
const multiplierStreamCompositionMbps = numberOfStreamsUhd * mbpsH264Uhd | ||
+ numberOfStreamsHd * mbpsH264Hd | ||
+ numberOfStreamsSd * mbpsH264Sd; | ||
const allRenditionsGbPerMinH264 = (multiplierStreamCompositionMbps * 60) / (8 * 1000); // GB (8 * 1000) per min (60 seconds) | ||
const allRenditionsGbPerMinAv1 = allRenditionsGbPerMinH264 * improvementsAv1H264; | ||
|
||
const oneTimeCosts = encodingCostPerMinuteAv1 + allRenditionsGbPerMinAv1 * ingressCostPerGb; | ||
const savingsPerViewH264 = egressCostPerGb * (uhdRenditionGbPerMinH264 - uhdRenditionGbPerMinAv1); | ||
const savingsPerViewH265 = egressCostPerGb * (uhdRenditionGbPerMinH265 - uhdRenditionGbPerMinAv1); | ||
|
||
const av1H264BreakEven = oneTimeCosts / savingsPerViewH264; | ||
const av1H265BreakEven = oneTimeCosts / savingsPerViewH265; | ||
|
||
if (isNaN(av1H264BreakEven) || isNaN(av1H265BreakEven)) { | ||
av1H264Element.text('--'); | ||
av1H265Element.text('--'); | ||
} else { | ||
av1H264Element.text(`${roundDecimals(av1H264BreakEven, 0)} views`); | ||
av1H265Element.text(`${roundDecimals(av1H265BreakEven, 0)} views`); | ||
} | ||
} | ||
|
||
// disable submit event | ||
const formElement = $('#av1-form'); | ||
formElement.on('submit', function(e) { | ||
e.preventDefault(); | ||
e.stopPropagation(); | ||
}); | ||
|
||
// handle input fields | ||
$('input#encodingCostPerMinute', formElement).val(encodingCostPerMinute) | ||
.on('input', inputEventHandler((value) => { | ||
encodingCostPerMinute = value; | ||
calculateBreakEvenPoints(); | ||
})); | ||
|
||
$('input#ingressCostPerGb', formElement).val(ingressCostPerGb) | ||
.on('input', inputEventHandler((value) => { | ||
ingressCostPerGb = value; | ||
calculateBreakEvenPoints(); | ||
})); | ||
|
||
$('input#egressCostPerGb', formElement).val(egressCostPerGb) | ||
.on('input', inputEventHandler((value) => { | ||
egressCostPerGb = value; | ||
calculateBreakEvenPoints(); | ||
})); | ||
|
||
$('input#numberOfStreamsUhd', formElement).val(numberOfStreamsUhd) | ||
.on('input', inputEventHandler((value) => { | ||
numberOfStreamsUhd = value; | ||
calculateBreakEvenPoints(); | ||
})); | ||
|
||
$('input#numberOfStreamsHd', formElement).val(numberOfStreamsHd) | ||
.on('input', inputEventHandler((value) => { | ||
numberOfStreamsHd = value; | ||
calculateBreakEvenPoints(); | ||
})); | ||
|
||
$('input#numberOfStreamsSd', formElement).val(numberOfStreamsSd) | ||
.on('input', inputEventHandler((value) => { | ||
numberOfStreamsSd = value; | ||
calculateBreakEvenPoints(); | ||
})); | ||
|
||
calculateBreakEvenPoints(); | ||
})(); | ||
|
||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.