То есть получается, что отдача одним файлом и браузерный декод, вышли намного эффективнее чем то, что мы делали через HLS. ¯\_( ツ)_/¯Разумеется.
Мы посмотрели кучу стриминговых сервисов, большая часть из них отдавала все свои стримы в формате m3u8, и никаких проблем при этом люди не испытывали. Соответственно назрела куча вопросов о том, как таки стоит делать и в чем могла быть ошибка и как это исправить на будущее.
1) Как правильно хостить файлы на сервере? Нужна ли разбивка при помощи HLS\DASH? Где-то видел что эти технологии нужно использовать в паре, так как каждая из них имеет свою браузерную поддержку.Для видеофайлов не требуется какой-то особый подход к размещению на диске. HLS поддерживается только мобильными браузерами (многими, но не всеми), а DASH не поддерживается никакими современными браузерами. Вам в любом случае придётся использовать javascript-плеер, который самостоятельно будет собирать поток из HLS/DASH и воспроизводить через media source, поэтому принципиальной разницы нет. Использовать и HLS, и DASH одновременно точно ни к чему.
2) Должны ли быть на сервере какие-то специфичные настройки, для эффективной отдачи статического медиа-контента?Да не особо. Так как у вас многогигабитный канал, можно попробовать настроить сетевую подсистему (если речь о Linux), а именно увеличить TCP-буферы, буферы отправки и получения, количество conntrack-соединений (может, ошибки соединения возникают по причине лимита conntrack? В dmesg заглядывали?).
3) Медиа-плеер. Возможно, причина ошибок связана с плеером, который использовали на клиенте?Может, безусловно. Плееры содержат достаточно сложный код: парсеры и демуксеры контейнеров, работа с HLS, media source, совместимость с разными браузерами.
Например, в этом проекте, люди заходили с телевизора, а на tizen flowplayer не работал, от слова совсем.Рекомендую попробовать clappr.io — один из немногих, корректно работающих на устаревшем браузере Blackberry.
4) Шифрование\защита файлов. Как по мне отдача чистых mp4 файлов, небезопасна от слова совсем.Зачем нужно защищать ваши файлы, если вы и так их проигрываете? Может, следует подумать о людях и об удобстве просмотра, и предоставить ссылку, которую можно открыть в нормальном видеоплеере, или скачать фильм в виде файла? Не понимаю эту дурацкую тенденцию.
function validate(pass) {
if (pass.length >= 8) {
if (/[^a-zA-Z0-9]/.test(pass)) {
return 'NO'
}
if (/(?=.*\d)(?=.*[a-z])(?=.*[A-Z])/.test(pass)) {
return 'YES'
}
}
return 'NO'
}
validate('123ASDDASD') // NO
validate('123qwe_ASD') // NO
validate('123qwe') // NO
validate('123qweASD') // YES
www.youtube.com/get_video_info?video_id=XXXXXX
XXXXXX
подставьте хэш видео, который в ссылке на ролик, типа youtu.be/BWCiWZtrWXU
после слеша.url_encoded_fmt_stream_map
. Его значение опять надо распаковать как URL-параметры. И из результата вытащить параметр url
– это ссылка на единый скачивабельный видеофайл.function getUrlParams(search) {
let hashes = search.slice(search.indexOf('?') + 1).split('&')
let params = {}
hashes.map(hash => {
let [key, val] = hash.split('=')
params[key] = decodeURIComponent(val)
})
return params
}
var s = '------'; // здесь длиннющая строка из ответа /get_video_info
var a = getUrlParams(s);
var b = getUrlParams(a.url_encoded_fmt_stream_map);
console.log(b.url); // эту ссылку открываем в браузере - это скачиваемый видеофайл
class Main extends Component {
state = {
isInitializing: true,
isSignedIn: false,
user: null,
};
async componentDidMount() {
// тут достаем токен, например, из cookie
const token = storage.get(TOKEN_KEY);
if (token) {
// тут сетим токен в заголовки по-умолчанию библиотеки для HTTP-запросов
api.setHeader('Authorization', `Bearer ${token}`);
const user = await api.getUser();
this.setState({ user, isSignedIn: true, isInitializing: false });
} else {
this.setState({ isInitializing: false });
}
}
render() {
const { isSignedIn, isInitializing } = this.state;
// на время инициализации показываем ProgressBar
if (isInitializing) return <ProgressBar />;
return (
<code lang="html">
<Router>
<div id = 'content'>
{isSignedIn ? (
<React.Fragment>
<Route path='/feed' exact component={Feed} />
<Redirect from='/' to="/feed" />
</React.Fragment>
) : (
<React.Fragment>
<Route path='/' exact component={Auth} />
<Redirect from="/feed" to="/" />
</React.Fragment>
)}
</div>
</Router>
</code>
);
}
}
const store = configureStore();
store.dispatch(init()); // начинаем процесс инициализации еще до монтирования приложения
ReactDOM.render(
<Provider store={store}>
<Main />
</Provider>, document.getElementById('main'),
);
class TodoList extends React.Component {
state = {
todos: [
'Commit',
'Push'
]
}
render() {
return <ul>
{this.state.todos.map(item => {
return <li>{ todo }</li>
}
</ul>
}
}
const todos = [
'Init',
'Commit',
'Push'
]
// Начальный стейт
<ul>
<li>Commit</li>
<li>Push</li>
</ul>
// Добавлен элемент
<ul>
<li>Init</li> // <- разница начинается здесь и до конца древа
<li>Commit</li>
<li>Push</li>
</ul>
const todos = [
'Commit',
'Push',
'Merge'
]
// Начальный стейт
<ul>
<li>Commit</li>
<li>Push</li>
</ul>
// Добавлен элемент
<ul>
<li>Commit</li>
<li>Push</li>
<li>Merge</li> <- разница начинается здесь, от начала и до сих по ничего не менялось
</ul>
<li>Commit</li>
и <li>Push</li>
не менялись, однако реакт недостаточно умён чтобы это понять. Чтобы помочь ему следует воспользоваться специальным пропом key={}
. Он может быть значением любого типа, единственно требование — значение должно стабильно идентифицировать соответствующие данные.class TodoList extends React.Component {
state = {
todos: [
{ id: 0, text: 'Commit' },
{ id: 1, text: 'Push' }
]
}
render() {
return <ul>
{this.state.todos.map(item => {
return <li key={todo.id}>{ todo.text }</li>
}
</ul>
}
}
const todos = [
{ id: 2, text: 'Init' },
{ id: 0, text: 'Commit' },
{ id: 1, text: 'Push' }
]
// Начальный стейт
<ul>
<li>Commit</li> // id 0
<li>Push</li> // id 1
</ul>
// Добавлен элемент
<ul>
<li>Init</li> // id 2 новый элемент отобразится в начале
<li>Commit</li> // id 0
<li>Push</li> // id 1
</ul>
Math.random()
в качестве ключа, так вы почти гарантировано будете всегда получать нестабильные идентификаторы.(function( nick, el) {
var L = nick.length
,cursor = 0
,arr
,timeout
,prob = 0.05 // вероятность "разгадывания" очередной позиции
,rate = 25 // частота "кадров" в секунду
,delay = 1000 / rate
;
function tweak() {
arr = nick.split('');
for(pos = cursor; pos < L; pos++) {
arr[pos] = String.fromCharCode(
1024 + Math.round( (1279 - 1024) * Math.random() )
);
}
el.innerText = arr.join('');
if( cursor === L) return; // done
if( Math.random() < prob) cursor++;
timeout = window.setTimeout( tweak, delay);
}
tweak();
})( "Sergei Sokolov", document.getElementById("effect") )
var self = this;
this.player.addEventListener('ended', function (){
self.nextSong();
console.log('Audio file ended');
});
this.player.addEventListener('ended', () => {
this.nextSong();
console.log('Audio file ended');
});
огромное количество тестов предлагают пройти их бесплатно, но вот результат после 20-30 минут мучения только за деньги
background-size: 600px 4px
— чтобы каждая полоска была равно 100px,background-repeat: repeat-x;
.container {
margin: auto;
background: #FFF;
max-width: 960px;
background-image: linear-gradient(to right,
#50B5AD 0%, #50B5AD 16.67%,
#FFBBB7 16.67%, #FFBBB7 33.33%,
#F8506C 33.33%, #F8506C 50%,
#ADB347 50%, #ADB347 66.67%,
#F5CE42 66.67%, #F5CE42 83.33%,
#A2DBD6 83.33%, #A2DBD6 100%);
background-size: 600px 4px;
background-repeat: repeat-x;
}
X / 1000 = Y
Where X is the value of the letter-spacing in Photoshop and Y is the value in em to use in CSS
Photoshop to em “formula”
X * S / 1000 = P
Where X is equal to the letter-spacing value in Photoshop, S is the font-size in pixels (which is equal to the value in point provided you’re working in 72dpi) and P is the resulted value in px to use in CSS
Photoshop to px “formula”