-
-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Help getting audio from audio context working #14
Comments
Hey! What you are doing here cannot work; you are first encoding the audio channel into a WebM file (that's what MediaRecorder does), and then piping that into the WebM as an audio chunk. The WebM muxer expects the raw codec data, e.g. an Opus frame, for example. Additionally, the audio is split into many small frames, whereas in your example, you're giving it the whole 188 kB chunk at once. I take it from your example that you want to sample the audio coming out of your audio context and add that to the WebM. What you want is a way to get an audio buffer that contains the output of your audio context. A nice way to do this is using the OfflineAudioContext, but this is probably not applicable in your case. What you can do instead is pipe your output (using I hope this helps! This might require some trial and error and doc-reading to get to work right, but I'm pretty sure this is the correct way. |
Thanks for getting back to me, that's a bit more complex than i was hoping.
I may end up sending both of them to the server and use ffmpeg.
…On Thu, 6 Apr 2023, 05:21 Vanilagy, ***@***.***> wrote:
Hey! What you are doing here cannot work; you are first encoding the audio
channel into a WebM file (that's what MediaRecorder does), and then piping
that into the WebM as an audio chunk. The WebM muxer expects the raw codec
data, e.g. an Opus frame, for example. Additionally, the audio is split
into many small frames, whereas in your example, you're giving it the whole
188 kB chunk at once.
I take it from your example that you want to sample the audio coming out
of your audio context and add that to the WebM. What you want is a way to
get an audio buffer that contains the output of your audio context. A nice
way to do this is using the OfflineAudioContext
<https://developer.mozilla.org/en-US/docs/Web/API/OfflineAudioContext>,
but this is probably not applicable in your case.
What you can do instead is pipe your output (using inAudio.get().connect)
into a ScriptProcessorNode
<https://developer.mozilla.org/en-US/docs/Web/API/ScriptProcessorNode>
or, using the newer API, into an AudioWorklet
<https://developer.mozilla.org/en-US/docs/Web/API/AudioWorkletNode> (I'd
use the ScriptProcessor). These get called periodically with new audio
data, provided to you as raw bytes representing the individual
audio samples for each channel. You can aggregate these into an array, and
when you're done recording, create an instance of AudioData
<https://developer.mozilla.org/en-US/docs/Web/API/AudioData/AudioData>
from the array. Then, pass this AudioData instance into the encode method
for an AudioEncoder. The AudioEncoder will spit out many small chunks, just
like the VideoEncoder, which you can then pipe into the muxer.
I hope this helps! This might require some trial and error and doc-reading
to get to work right, but I'm pretty sure this is the correct way.
—
Reply to this email directly, view it on GitHub
<#14 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAORBIAQP2HNCVLAQDUUMV3W7XEKTANCNFSM6AAAAAAWUGP5UU>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
It sounds complex, but it's not when you've written it once! If it improves UX, I'd definitely try to mux it into one thing. All you need to do is push to an array, then create AudioData and encode that. I know it sounds intimidating, but it's one of these things that end up being like 25 lines of code in the end and seem easy in hindsight:) But hey, do whatever you think works best. Still appreciate you using my lib! |
I have given up :( I spent the best part of 2 days but this stuff is too complex for me... I got this far
The AudioWorkletProcessor is posting back the wrong format and the audioEncoder always complains that its not AudioData format. I have tried a million things with the 'help' of gpt-4 but no luck. And I suspect I am doing it wrong calling it right at the end too, perhaps it needs to be on a rolling basis like the video frames? |
Any chance I could pay you to make it work ? |
Okay hold on, I realize that your initial attempt should actually work if we simply use the audio stream differently: const audioCtx = CABLES.WEBAUDIO.createAudioContext(op);
const streamAudio = audioCtx.createMediaStreamDestination();
inAudio.get().connect(streamAudio);
audioTrack = streamAudio.stream.getAudioTracks()[0];
muxer = new WebMMuxer({
"target": "buffer",
"video": {
"codec": "V_VP9",
"width": inWidth.get() / CABLES.patch.cgl.pixelDensity,
"height": inHeight.get() / CABLES.patch.cgl.pixelDensity,
"frameRate": fps
},
"audio": {
"codec": "A_OPUS",
"sampleRate": 48000,
"numberOfChannels": 2
},
"firstTimestampBehavior": "offset" // Because we're directly pumping a MediaStreamTrack's data into it
});
videoEncoder = new VideoEncoder({
"output": (chunk, meta) => { return muxer.addVideoChunk(chunk, meta); },
"error": (e) => { return op.error(e); }
});
videoEncoder.configure({
"codec": "vp09.00.10.08",
"width": inWidth.get() / CABLES.patch.cgl.pixelDensity,
"height": inHeight.get() / CABLES.patch.cgl.pixelDensity,
"framerate": 29.7,
"bitrate": 5e6
});
if (audioTrack) {
op.log('we HAVE AUDIO !!!!!!!!!!!!!!!!!!');
audioEncoder = new AudioEncoder({
output: (chunk, meta) => muxer.addAudioChunk(chunk, meta),
error: e => console.error(e)
});
audioEncoder.configure({
codec: 'opus',
numberOfChannels: 2,
sampleRate: 48000, //todo should have a variable
bitrate: 128000,
});
// Create a MediaStreamTrackProcessor to get AudioData chunks from the audio track
let trackProcessor = new MediaStreamTrackProcessor({ track: audioTrack });
let consumer = new WritableStream({
write(audioData) {
audioEncoder.encode(audioData);
audioData.close();
}
});
trackProcessor.readable.pipeTo(consumer);
} I maintained most of the code from my demo, using a MediaStreamTrackProcessor to get the AudioData from the stream, which you created. This should do the job! Not sure why I didn't think of this sooner. I guess my question would be, why did you comment out this original code from the demo - did you think it's not applicable in your case? That said, I didn't test or run this code, but this should give you a good baseline. To recap: We get the audio track from the MediaStreamDestinationNode, then pipe that into a MediaStreamTrackProcessor which gives us, bit by bit, instances of AudioData as the audio is playing. This AudioData is then being fed into the AudioEncoder, which spits out EncodedAudioChunks, which are then sent to the muxer. Get back to me and tell me if this worked for you (maybe you'll need to adjust it a bit, still). If all of this still doesn't work, we can discuss me pouring in a bit more of my time to help you! |
Thanks I got it working finally. I had to play around to make sure it was not recording passed the length of the video as I am recording the video frame by frame (slower than realtime) so the sound was carrying on making the video longer and paused at the end. Thanks for this great muxer! |
Let's go! |
I am wondering if anyone can help me mux video and audio (not from the microphone) together? Below is a snippet of some of the code I am using inside a cables.gl op file. I have managed to feed canvas frames one by one to the video to get perfectly formed videos with no missing frames. However when I add the audio the video is not viewable, when I ffmpeg convert it to mp4 there is no audio.
The text was updated successfully, but these errors were encountered: