用于 html 媒体源视频音频异步的 FFmpeg 分段
我是一个新提问者,我从来没有必要在这里问任何事情,但我对这件事感到有点无能为力,因为我以前从未使用过它。
我想建立自己的实时视频流和一个新的视频播放器。
我的想法是,在 FFmpeg 的帮助下,我正在进行实时视频分段,其中分段具有统一的长度。我经常将它们写入列表文件中,并使用 HTML 媒体源扩展来处理它们。
问题出在音频和视频的同步上。我不确定,要么视频会随着时间的推移而延迟,要么音频会比应有的速度更快。如果我刷新页面/滚动视频,一切都会重置,但随着时间的推移,同步会再次混乱。 我不知道是什么原因造成的。我尝试搜索许多论坛帖子,但没有找到任何解决方案。
我不想使用 HLS 或 DASH。
操作系统:Win10 浏览器:Chrome 98.0.4758.102
这是我的 FFmpeg 命令:
Ffmpeg Command (version 2022-02-24-git-8ef03c2ff1-full_build-www.gyan.dev):
ffmpeg -re -i input.mkv -map 0:v:0 -map 0:a:0 -c:v libx264 -pix_fmt yuv420p -vf scale=1280:720 -c:a copy -sample_rate 44100 -segment_time 2 -r 30 -g 1 -sc_threshold 0 -force_key_frames ""expr:gte(t,n_forced*1)"" -flags +cgop+low_delay -f segment -segment_format_options movflags=+empty_moov+omit_tfhd_offset+frag_keyframe+default_base_moof+isml -segment_list_type ext -segment_list {segment_file_dir} output_%d.mp4
HTML 来源:
<video muted controls></video>
<script>
segments = [];
last_segment = 0;
mimeCodec = 'video/mp4; codecs="avc1.64000d,mp4a.40.2"';
timestampOffset = 0;
video = document.querySelector('video');
mediaSource = new MediaSource();
video.src = URL.createObjectURL(mediaSource);
sourceBuffer = null;
segment_process = false;
mediaSource.addEventListener('sourceopen', function(){
sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);
sourceBuffer.mode = 'segments';
// Initialization
const init_xhr = new XMLHttpRequest();
init_xhr.open('get', 'init.mp4');
init_xhr.setRequestHeader('Cache-Control', 'no-cache');
init_xhr.responseType = 'arraybuffer';
init_xhr.send();
init_xhr.onload = function(){
sourceBuffer.appendBuffer(init_xhr.response);
sourceBuffer.addEventListener('updateend', event = function(){
sourceBuffer.removeEventListener('updateend', event);
sourceBuffer.timestampOffset = 0;
sourceBuffer.remove(0,1);
manifestInitialization(function(){
if (segments.length > 0 && segment_process == false){
segments = segments.slice(-1);
segmentProccess();
}
});
});
};
}.bind(mediaSource));
setInterval(function(){
manifestInitialization(function(){
if (segments.length > 0 && segment_process == false){
segmentProccess();
}
});
},500);
// Manifest Initialization
function manifestInitialization(callback){
const manifest_xhr = new XMLHttpRequest();
manifest_xhr.open('get', 'ehls/manifest');
manifest_xhr.setRequestHeader('Cache-Control', 'no-cache');
manifest_xhr.responseType = 'text';
manifest_xhr.send();
manifest_xhr.onload = function(e){
lines = manifest_xhr.responseText.split(/\r?\n/);
lines.forEach(function(line){
line = line.split(';');
const segment = {id: Number(line[0]), file: line[1], duration: Number(line[2]), type: Number(line[3])};
if (!objectInArray(segment, segments) && segment.id > last_segment){
segments.push(segment);
last_segment = segment.id;
}
});
callback();
};
};
// Segment Load
function segmentProccess(){
segment_process = true;
segment = segments.shift();
const segment_xhr = new XMLHttpRequest();
segment_xhr.open('get', 'ehls/' + segment.file);
segment_xhr.setRequestHeader('Cache-Control', 'no-cache');
segment_xhr.responseType = 'arraybuffer';
segment_xhr.send();
segment_xhr.onload = function(){
timestampOffset += segment.duration;
sourceBuffer.appendBuffer(segment_xhr.response);
sourceBuffer.addEventListener('updateend', event = function(){
sourceBuffer.removeEventListener('updateend', event);
sourceBuffer.timestampOffset = timestampOffset;
video.oncanplay = function(){
video.play();
};
if (segments.length > 0){
segmentProccess();
}
else{
segment_process = false;
}
});
};
};
// Object in array
function objectInArray(sObj, array){
returnV = false;
array.forEach(function(aObj){
if (JSON.stringify(sObj) == JSON.stringify(aObj)){
returnV = true;
}
});
return returnV;
};
</script>
论坛中的一些人提到chrome://media-internals/,使用它我也注意到这个。
以前有人用过这个吗?你们知道我该如何处理这个同步问题吗?
感谢您提前的答复!
I'm a new questioner, I never had to ask anything here, but I feel a bit clueless about this thing as I never worked with it before.
I would like to build my own live video stream and a new video player for it.
My idea is that with the help of FFmpeg I'm doing live video segmentations where the segments have uniform lengths. I'm constantly writing these in a list file, handling them with an HTML Media Source extension.
The problem is with the sync of audio and video. Either the video gets delayed over time or the audio comes faster than it should, I'm not sure. If I refresh the page/scroll through the video, everything resets, but as time goes on, the sync gets messed up again.
I have no idea what could cause this. I tried to search through many forum posts, but I did not find any solution for it.
I don't want to use HLS or DASH.
OS: Win10 Browser: Chrome 98.0.4758.102
This is my FFmpeg command:
Ffmpeg Command (version 2022-02-24-git-8ef03c2ff1-full_build-www.gyan.dev):
ffmpeg -re -i input.mkv -map 0:v:0 -map 0:a:0 -c:v libx264 -pix_fmt yuv420p -vf scale=1280:720 -c:a copy -sample_rate 44100 -segment_time 2 -r 30 -g 1 -sc_threshold 0 -force_key_frames ""expr:gte(t,n_forced*1)"" -flags +cgop+low_delay -f segment -segment_format_options movflags=+empty_moov+omit_tfhd_offset+frag_keyframe+default_base_moof+isml -segment_list_type ext -segment_list {segment_file_dir} output_%d.mp4
HTML Source:
<video muted controls></video>
<script>
segments = [];
last_segment = 0;
mimeCodec = 'video/mp4; codecs="avc1.64000d,mp4a.40.2"';
timestampOffset = 0;
video = document.querySelector('video');
mediaSource = new MediaSource();
video.src = URL.createObjectURL(mediaSource);
sourceBuffer = null;
segment_process = false;
mediaSource.addEventListener('sourceopen', function(){
sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);
sourceBuffer.mode = 'segments';
// Initialization
const init_xhr = new XMLHttpRequest();
init_xhr.open('get', 'init.mp4');
init_xhr.setRequestHeader('Cache-Control', 'no-cache');
init_xhr.responseType = 'arraybuffer';
init_xhr.send();
init_xhr.onload = function(){
sourceBuffer.appendBuffer(init_xhr.response);
sourceBuffer.addEventListener('updateend', event = function(){
sourceBuffer.removeEventListener('updateend', event);
sourceBuffer.timestampOffset = 0;
sourceBuffer.remove(0,1);
manifestInitialization(function(){
if (segments.length > 0 && segment_process == false){
segments = segments.slice(-1);
segmentProccess();
}
});
});
};
}.bind(mediaSource));
setInterval(function(){
manifestInitialization(function(){
if (segments.length > 0 && segment_process == false){
segmentProccess();
}
});
},500);
// Manifest Initialization
function manifestInitialization(callback){
const manifest_xhr = new XMLHttpRequest();
manifest_xhr.open('get', 'ehls/manifest');
manifest_xhr.setRequestHeader('Cache-Control', 'no-cache');
manifest_xhr.responseType = 'text';
manifest_xhr.send();
manifest_xhr.onload = function(e){
lines = manifest_xhr.responseText.split(/\r?\n/);
lines.forEach(function(line){
line = line.split(';');
const segment = {id: Number(line[0]), file: line[1], duration: Number(line[2]), type: Number(line[3])};
if (!objectInArray(segment, segments) && segment.id > last_segment){
segments.push(segment);
last_segment = segment.id;
}
});
callback();
};
};
// Segment Load
function segmentProccess(){
segment_process = true;
segment = segments.shift();
const segment_xhr = new XMLHttpRequest();
segment_xhr.open('get', 'ehls/' + segment.file);
segment_xhr.setRequestHeader('Cache-Control', 'no-cache');
segment_xhr.responseType = 'arraybuffer';
segment_xhr.send();
segment_xhr.onload = function(){
timestampOffset += segment.duration;
sourceBuffer.appendBuffer(segment_xhr.response);
sourceBuffer.addEventListener('updateend', event = function(){
sourceBuffer.removeEventListener('updateend', event);
sourceBuffer.timestampOffset = timestampOffset;
video.oncanplay = function(){
video.play();
};
if (segments.length > 0){
segmentProccess();
}
else{
segment_process = false;
}
});
};
};
// Object in array
function objectInArray(sObj, array){
returnV = false;
array.forEach(function(aObj){
if (JSON.stringify(sObj) == JSON.stringify(aObj)){
returnV = true;
}
});
return returnV;
};
</script>
Some people in forums mentioned chrome://media-internals/, using that I also noticed this.
Did anyone work with this before? Do you guys have any idea how could I deal with this sync problem?
Thank you for your answers in advance!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论