Play Separated Video & Audio Files
namdevel opened this issue · 71 comments
hello, how to play separated video and audio files
example i have 2 file
- video without audio sound
- audio sound of video
You can try this, but why not use ffmpeg to merge them?
class SeparatedAudio implements PlayerPlugin {
key = 'hello'
name = 'oplayer-plugin-hello'
version = __VERSION__
private $audio: HTMLAudioElement = document.createElement('audio')
constructor(private audioSrc: string) {
this.$audio.src = this.audioSrc
}
apply(player: Player) {
player.on(['play', 'pause'], ({ type }) => {
//@ts-ignore
this.$audio[type]()
this.$audio.currentTime = player.currentTime
})
player.on(['seeking', 'error'], () => {
this.$audio.pause()
})
const loading = $.cls('loading')
player.on(() => {
if (player.$root.classList.contains(loading)) {
this.$audio.pause()
} else {
if (player.isPlaying && this.$audio.paused) {
this.$audio.play()
}
}
})
player.on('seeked', () => {
this.$audio.currentTime = player.currentTime
if (player.isPlaying) this.$audio.play()
})
player.on('volumechange', () => {
this.$audio.volume = player.volume
})
player.on('ratechange', () => {
this.$audio.playbackRate = player.playbackRate
})
// .... other
}
destroy() {
this.$audio.remove()
URL.revokeObjectURL(this.$audio.src)
}
}
@shiyiya and why i seeing this, player.context.ui.menu.notice is not a function
use latest version @oplayer/core
@oplayer/ui
yes, here is my code
<div id="oplayer"></div>
<script src="https://cdn.jsdelivr.net/npm/@oplayer/core@latest/dist/index.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@oplayer/ui@latest/dist/index.core.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@oplayer/core@latest/dist/index.ui.js"></script>
<script>
const audio = new Audio("https://ngewibu.test/stream/audio/1/12771946.mp4");
audio.load();
const UIPlayer = OPlayer.ui({
fullscreen: true,
coverButton: true,
miniProgressBar: true,
autoFocus: true,
forceLandscapeOnFullscreen: true,
screenshot: true,
pictureInPicture: false,
showControls: "always",
keyboard: { focused: true, global: false },
settings: ["loop"],
theme: { primaryColor: "#ffc107" },
speeds: ["2.0", "1.75", "1.25", "1.0", "0.75", "0.5"],
slideToSeek: "none",
controlBar: false, // | { back: 'always' | 'fullscreen' } // appbar
topSetting: false, //show setting icon on appbar
/* Default value, Optional */
/* Custom options */
subtitle: {
color: "white",
fontSize: 30,
fontFamily: "",
source: [
{
name: "Bahasa Indonesia",
default: true,
src: "sub.vtt",
},
],
},
thumbnails: {
number: 100,
src: "https://oplayer.vercel.app/thumbnails.jpg",
},
});
const vplayer = OPlayer.make("#oplayer", {
source: {
src: "https://ngewibu.test/stream/videos/1/12771946.mp4",
poster:
"https://ngewibu.test/static/img/ogv/5d04814e48ef572e97cb18bbe928bbf8086c38cf.png@720w_405h_1e_1c_90q.webp",
title: "Ngewibu.test",
},
}).use([UIPlayer]);
vplayer.create();
vplayer.context.ui.menu.notice("test hello"); // test notice
</script>
<script>
function playMedia() {
audio.play();
}
function pauseMedia() {
audio.pause();
}
function seekMedia() {
audio.currentTime = vplayer.currentTime;
}
// Keep audio and video in sync
function syncMedia() {
audio.currentTime = vplayer.currentTime;
}
// Event listeners
vplayer.on("play", playMedia);
vplayer.on("durationchange", syncMedia);
vplayer.on("pause", pauseMedia);
vplayer.on("seeked", seekMedia);
</script>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="oplayer"></div>
<script src="https://cdn.jsdelivr.net/npm/@oplayer/core@latest/dist/index.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@oplayer/ui@latest/dist/index.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@oplayer/hls@latest/dist/index.hls.js"></script>
<script>
class SeparatedAudio {
constructor(audioSrc) {
this.audioSrc = audioSrc
this.key = 'hello'
this.name = 'oplayer-plugin-hello'
this.$audio = document.createElement('audio')
this.$audio.src = this.audioSrc
}
apply(player) {
player.on(['play', 'pause'], ({ type }) => {
this.$audio[type]()
this.$audio.currentTime = player.currentTime
})
player.on(['seeking', 'error'], () => {
this.$audio.pause()
})
const loading = OPlayer.$.cls('loading')
player.on(() => {
if (player.$root.classList.contains(loading)) {
this.$audio.pause()
} else {
if (player.isPlaying && this.$audio.paused) {
this.$audio.play()
}
}
})
player.on('seeked', () => {
this.$audio.currentTime = player.currentTime
if (player.isPlaying) this.$audio.play()
})
player.on('volumechange', () => {
this.$audio.volume = player.volume
})
player.on('ratechange', () => {
this.$audio.playbackRate = player.playbackRate
})
// .... other
}
destroy() {
this.$audio.remove()
URL.revokeObjectURL(this.$audio.src)
}
}
const UI = OUI({
fullscreen: true,
coverButton: true,
miniProgressBar: true,
autoFocus: true,
forceLandscapeOnFullscreen: true,
screenshot: true,
pictureInPicture: false,
showControls: 'always',
keyboard: { focused: true, global: false },
settings: ['loop'],
theme: { primaryColor: '#ffc107' },
speeds: ['2.0', '1.75', '1.25', '1.0', '0.75', '0.5'],
slideToSeek: 'none',
controlBar: false, // | { back: 'always' | 'fullscreen' } // appbar
topSetting: false, //show setting icon on appbar
/* Default value, Optional */
/* Custom options */
subtitle: {
color: 'white',
fontSize: 30,
fontFamily: '',
source: [
{
name: 'Bahasa Indonesia',
default: true,
src: 'sub.vtt',
},
],
},
thumbnails: {
number: 100,
src: 'https://oplayer.vercel.app/thumbnails.jpg',
},
})
const vplayer = OPlayer.make('#oplayer', {
source: {
src: 'https://ngewibu.test/stream/videos/1/12771946.mp4',
poster: 'https://ngewibu.test/static/img/ogv/5d04814e48ef572e97cb18bbe928bbf8086c38cf.png@720w_405h_1e_1c_90q.webp',
title: 'Ngewibu.test',
},
})
.use([UI, new SeparatedAudio('https://ngewibu.test/stream/audio/1/12771946.mp4')])
.create()
vplayer.context.ui.notice('test hello') // test notice
</script>
</body>
</html>
thank you @shiyiya
can this player add watermark image ?
like in top left or whenever custom position ?
thank you @shiyiya can this player add watermark image ? like in top left or whenever custom position ?
document.createlm(img)
img.src =''
img.style.position= ''
vplayer.$root.appendChild(img)
thank you
var img = document.createElement("img");
img.src = "layer.png";
img.style.position = "absolute";
img.style.top = "10px";
img.style.right = "10px";
img.style.width = "200px";
img.style.height = "auto";
vplayer.$root.appendChild(img);
i hope you can add watermark in Oplayer options in future
I wanted to take a moment to express my sincerest thanks for your incredible work.
i hope you can add watermark in Oplayer options in future I wanted to take a moment to express my sincerest thanks for your incredible work.
I will consider it, thank u 😄
i'm seeing this error while take a screenshot
oplayer/examples/standalone/main.ts
Line 51 in b86db76
how to add custom slide in settings menu ?
like loop menu but custom and handle on off
how to add custom slide in settings menu ? like loop menu but custom and handle on off
i'm confused ^_^
player.context.ui.setting.register()
tester.html:117 Uncaught TypeError: Cannot read properties of undefined (reading 'setting')
oh sorry my mistakes
i try in my real iOS device and it's same , that was appeared while in the fullscreen mode
i try in my real iOS device and it's same , that was appeared while in the fullscreen mode
Yes, this is normal, ios can only video in full screen
AAh ok, thank you
is that available on jsdeliver version ?
because i'm not use npm
<script src="https://cdn.jsdelivr.net/npm/@oplayer/core@latest/dist/index.min.js"></script>
replace
<script src="https://cdn.jsdelivr.net/npm/@oplayer/core@1.2.26-beta.1/dist/index.min.js"></script>
how to add multiple thumbnails
image 1
image 2
x : 10
y: 10
x_sizes: 160
y_sizes: 90
how to add multiple thumbnails
image 1 image 2
x : 10 y: 10
x_sizes: 160 y_sizes: 90
https://oplayer.vercel.app/ui/#thumbnails
default size is 160 90,
thumbnails = {
src: ['image1','image2'],
x: 10,
y: 6,
number: 60
}
not work, the thumbnails not same with video time
not work, the thumbnails not same with video time
Can you provide an example?
403
If you can provide accessible videos and pictures, I can try to fix the error tomorrow
forbidden access from videoplayer or from url ?
yea
forbidden access from videoplayer or from url ?
not work, the thumbnails not same with video time
Of course, it is possible that the number of thumbnails generated is small and the gaps are too large so they are not that accurate
video duration is 24:00 minutes
thumbnails = {
src: ['image1','image2'],
x: 10,
y: 10,
number: 192
}
Can you provide a complete example, fork template, add video address and thumbnails configuration
https://codesandbox.io/s/oplayer-m4inym
I'm not sure if it's normal, it depends on the number of thumbnails you generate, for example, if the video is 10 minutes long and 2 thumbnails are generated, then it's quite inaccurate.
If your video is 24 minutes long and there are only 200 thumbnails, then the error of one thumbnail is 24*60/200=13.xxs. As long as the image exists before or after 13s of the video, it means it is normal.
OK, it is certain that it should be an issue with oplayer.
But to fix it you need to provide videos and thumbnails 😂
Can you provide a complete example, fork template, add video address and thumbnails configuration https://codesandbox.io/s/oplayer-m4inym
i has been add
hmm how to save code that i has been modified ?
hmm how to save code that i has been modified ?
just ctrl+s and then share the link to me in the top right corner, or just copy the link in the address bar?
you can click open sandbox in bottom right
it's like the thumbnail is 10s faster than video
i mean the 3:53 thumnails is appeared on 3:43
i got error, Maybe I don't have access?
MEDIA_ERR_SRC_NOT_SUPPORTED
Access Denied
You don't have permission to access "http://upos-bstar1-mirrorakam.akamaized.net/iupxcodeboss/65/hb/n230403er32xsq8sww4izs2q666shb65-1-241210110000.m4s?"
it's seem your country blocked by bilibili server
you from china why bilibili blocked your country
bilibili also from china
Featured 🤡
I'll fixing it tomorrow!
I am here late at night
ok, thank you
this is the json about thumbnail on bilibili
{
"code": 0,
"message": "0",
"ttl": 1,
"data": {
"pv_data": "https://pic.bstarstatic.com/videoshot/n230403er32xsq8sww4izs2q666shb65.bin",
"x_len": 10,
"y_len": 10,
"x_size": 160,
"y_size": 90,
"images": [
"https://pic.bstarstatic.com/videoshot/n230403er32xsq8sww4izs2q666shb65.jpg",
"https://pic.bstarstatic.com/videoshot/n230403er32xsq8sww4izs2q666shb65-1.jpg"
]
}
}
You can try pulling the bilibili video to the end to see if the video and thumbnails match。
the thumbnails match
number182, 3:53match but ed maybe not match
No idea
not work, the thumbnails not same with video time
Hey, you can play both at the same time, but it won't sync perfectly between audio and video. There will be slightly out of sync.
There is 1 solution for this (without doing merging both in the server), it is using mpeg dash. But you would need info such as bandwidth, etc....
Here is an example of how a mpeg dash file look like:
<?xml version="1.0"?>
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" minBufferTime="PT1M" type="static" mediaPresentationDuration="PT0H49M30S">
<Period duration="PT0H49M30S">
<AdaptationSet id="1" group="1" par="16:9" segmentAlignment="true" subsegmentAlignment="true" subsegmentStartsWithSAP="1" maxWidth="3840" maxHeight="2160" maxFrameRate="16000/656" startWithSAP="1">
<Representation id="0" mimeType="video/mp4" codecs="avc1.640033" width="3840" height="2160" frameRate="16000/656" sar="1:1" bandwidth="7890794">
<BaseURL>Video URL here</BaseURL>
<SegmentBase indexRangeExact="true" indexRange="995-8154">
<Initialization range="0-994"/>
</SegmentBase>
</Representation>
</AdaptationSet>
<AdaptationSet id="2" group="2" subsegmentAlignment="true" subsegmentStartsWithSAP="1" segmentAlignment="true" startWithSAP="1">
<Representation id="8" mimeType="audio/mp4" codecs="mp4a.40.2" bandwidth="180677">
<BaseURL>Audio URL</BaseURL>
<SegmentBase indexRangeExact="true" indexRange="934-8105">
<Initialization range="0-933"/>
</SegmentBase>
</Representation>
</AdaptationSet>
</Period>
</MPD>
If you managed to create a file like that, just use a player that support mpeg dash then audio and video should be sync'ed
how to get codecs type ?
how to get codecs type ?
ffmpeg
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="referrer" content="no-referrer" />
<title>Start streaming now using OPlayer - Free HTML5 Player</title>
<style>
p {
margin: 0;
}
h4 {
margin: 10px 0 4px;
}
.close {
position: absolute;
right: 10px;
top: 10px;
cursor: pointer;
font-size: 1em;
}
</style>
</head>
<body style="margin: 0; background-color: #000">
<div id="oplayer" style="width: 100vw; height: 100vh"></div>
<script src="https://cdn.jsdelivr.net/npm/@oplayer/core@latest/dist/index.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@oplayer/ui@latest/dist/index.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@oplayer/hls@latest/dist/index.hls.js"></script>
<script src="https://cdn.jsdelivr.net/npm/mp4box@0.5.2/dist/mp4box.all.min.js"></script>
<script>
const query = document.location.search.substring(1)
window.player = OPlayer.make('#oplayer', {
source: {
src: 'https://ohplayer.netlify.app/%E5%90%9B%E3%81%AE%E5%90%8D%E3%81%AF.mp4',
},
playbackRate: localStorage.getItem('@oplayer/UserPreferences/speed') || 1,
volume: localStorage.getItem('@oplayer/UserPreferences/volume') || 1,
})
.use([
OUI({ keyboard: { global: true } }),
OHls(() => true),
{
apply(player) {
var mp4box = MP4Box.createFile()
mp4box.onReady = showVideoInfo
fetch(player.options.source.src, {
headers: {
range: 'bytes=0-50000',
},
})
.then((it) => it.arrayBuffer())
.then((arrayBuffer) => {
arrayBuffer.fileStart = 0
mp4box.appendBuffer(arrayBuffer)
})
let box
player.context.ui.keyboard.register({
v: () => {
box.style.display = box.style.display == 'none' ? 'block' : 'none'
},
})
function showVideoInfo(info) {
console.log(info)
const videoTrack = info.tracks[0]
const audioTrack = info.tracks[1]
box = document.createElement('div')
box.innerHTML = `
<p>${'Duration: ' + parseInt(info.duration / 1000) + 's'}</p>
<p>${'Brands: ' + info.brands.join(',')}</p>
<h4>Video Metadata</h4>
<p>${'Video Codec: "' + videoTrack.codec + '"; nb_samples: ' + videoTrack.nb_samples}</p>
<p>${videoTrack.name + ': size: ' + videoTrack.size + '; bitrate: ' + videoTrack.bitrate}</p>
<h4>Audio Metadata</h4>
<p>${'Audio Codec: "' + audioTrack.codec + '"; nb_samples: ' + audioTrack.nb_samples}</p>
<p>${audioTrack.name + ': size: ' + audioTrack.size + '; bitrate: ' + audioTrack.bitrate}</p>
<div class="close">[x]</div>
`
box.style.cssText = `
position: absolute;
left: 0;
top: 0;
padding: 20px;
background-color: rgba(0,0,0, .5);
color: #54ab3d;
color: var(--primary-color);
font-size: 0.875em;
border-radius: 4px;`
player.context.ui.$root.appendChild(box)
}
},
},
])
.create()
.on(console.log)
</script>
</body>
</html>
ok thank you, any update about thumbnails ??
ok thank you, any update about thumbnails ??
I don't think oplayer is wrong.