WalkerKnapp / devolay

A Java binding for the Newtek NDI(tm) SDK.
Apache License 2.0
54 stars 17 forks source link

Stream NDI to a RTMP feed? #14

Closed arisprung closed 3 years ago

arisprung commented 4 years ago

Is there any way I can send the NDI stream to an RTMP end point? I see the "SendVideoExample" but where is it sending to? I must be missing something here....

bdunkleysmith commented 4 years ago

I'm no NDI/RTMP expert @arisprung but I have used @WalkerKnapp devolay in an app I have developed. But my understanding is that NDI is really just a transport mechanism for use over a LAN and you would need another means of consuming the NDI feed and send it to an RTMP endpoint. For instance in my case, my app can send the NDI content which I consume in vMix and which sends it to the RTMP endpoint. So the "SendVideoExample" just sends it "to" the LAN and you could check it is being sent by viewing it in Newtek's Studio Monitor which is included in their NDI 4 Tools.

arisprung commented 4 years ago

Thanks @bdunkleysmith ! That clarifies alot! "SendVideoExample" is sending a sample stream and I can see the stream in my OBS - below sceenshot(similar to your Vmix). I am looking for a way to programmatically receive a NDI and send that stream to an RTMP endpoint. Any idea?

Screen Shot 2020-09-18 at 11 23 39 AM
arisprung commented 4 years ago

ok after further investigation I can use the "RecvExample" to retrive NDI . Any idea how to take the "DevolayVideoFrame" and "DevolayAudioFrame" and stream to RTMP ? Maybe FFMPEG?

bdunkleysmith commented 4 years ago

Sorry @arisprung, that's way beyond my scope of knowledge. But perhaps @moster67 who had a joint interest in Issue #7 and I know is very knowledgeable in FFMPEG, may be able to provide some guidance if he's still active here.

arisprung commented 4 years ago

Thx @bdunkleysmith appreciate your assistance!

WalkerKnapp commented 4 years ago

@bdunkleysmith is right that NDI is just a transport, and that you'll need to convert from the raw video and auto given by DevolayVideoFrame and DevolayAudioFrame. I'd recommend javacpp-ffmpeg for this. It is essentially a 1-1 translation of the ffmpeg c api. I'm not very knowledgeable about the API, but there are a few published examples of streaming RTMP in C, and, even though it's not complete, you can cross reference how I use the Java api to save an NDI stream to a file. It is converting from NDI -> a saved video file, but that should translate fairly well to converting from NDI -> an RTMP stream.

WalkerKnapp commented 4 years ago

(or, you might want to use a wrapper around JavaCPP, like ffmpeg4j. It will be easier to write, but it won't be as direct of a translation between C and Java)

arisprung18 commented 4 years ago

Thx @WalkerKnapp ! This is great stuff! I started my research and ran the "RecordingExample" and got the below errors. Any idea?

Waiting for sources... Connecting to source: DESKTOP-GQNH46Q (Ari PC output) [file @ 0x7fa5e0e5db80] Setting default whitelist 'file,crypto,data' [mp4 @ 0x7fa5e130ae00] Could not find tag for codec none in stream #0, codec not currently supported in container Exception in thread "main" java.lang.IllegalStateException: Failed to write content header: Invalid argument at servlet.recording.JavaCPPDynamicRecorder.setup(JavaCPPDynamicRecorder.java:236) at servlet.recording.RecordingExample.main(RecordingExample.java:43)

Process finished with exit code 1

WalkerKnapp commented 4 years ago

I just pushed an update that should fix this. I am definitely not an expert with ffmpeg, so let me know if there are any other issues.

arisprung18 commented 4 years ago

Thx for your help. Getting the below error when running "RecordingExample":

Task :examples:RecordingExample.main() Connecting to source: DESKTOP-GQNH46Q (Ari PC output) [file @ 0x7fc1d394a4c0] Setting default whitelist 'file,crypto' [SWR @ 0x7fc1d7000000] Using fltp internally between filters x265 [info]: HEVC encoder version 0.0 x265 [info]: build info [Mac OS X][clang 8.1.0][64 bit] 8bit+10bit+12bit x265 [info]: using cpu capabilities: MMX2 SSE2Fast LZCNT SSSE3 SSE4.2 AVX FMA3 BMI2 AVX2 x265 [info]: Main profile, Level-3.1 (Main tier) x265 [info]: Thread pool created using 8 threads x265 [info]: Slices : 1 x265 [info]: frame threads / pool features : 1 / wpp(12 rows) x265 [info]: Coding QT: max CU size, min CU size : 64 / 8 x265 [info]: Residual QT: max TU size, max depth : 32 / 1 inter / 1 intra x265 [info]: ME / range / subpel / merge : hex / 57 / 2 / 3 x265 [info]: Keyframe min / max / scenecut / bias: 1 / 250 / 40 / 5.00 x265 [info]: Lookahead / bframes / badapt : 20 / 4 / 2 x265 [info]: b-pyramid / weightp / weightb : 1 / 1 / 0 x265 [info]: References / ref-limit cu / depth : 3 / off / on x265 [info]: AQ: mode / str / qg-size / cu-tree : 2 / 1.0 / 32 / 1 x265 [info]: Rate Control / qCompress : CRF-28.0 / 0.60 x265 [info]: tools: rd=3 psy-rd=2.00 early-skip rskip signhide tmvp b-intra x265 [info]: tools: strong-intra-smoothing lslices=4 deblock sao

WalkerKnapp commented 4 years ago

That's the normal output for it creating a recording. Did it crash after it displayed that? When you stop the source, it should stop on its own and write the output file.

WalkerKnapp commented 3 years ago

I am going to go ahead and close this as stale for now. Feel free to reopen this if you have more information or if your solution didn't work out.