ffmpeg.js 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. const { MediaStreamTrack, randomPort, RTCPeerConnection, RTCRtpCodecParameters, RtpPacket } = require('werift')
  2. const { Server } = require('ws')
  3. const { createSocket } = require('dgram')
  4. const { exec } = require('child_process')
  5. // open answer.html
  6. const server = new Server({ port: 8888 });
  7. console.log("start");
  8. server.on("connection", async (socket) => {
  9. console.log('客户端连接');
  10. const payloadType = 96;
  11. const pc = new RTCPeerConnection({
  12. codecs: {
  13. audio: [],
  14. video: [
  15. new RTCRtpCodecParameters({
  16. mimeType: "video/h264",
  17. clockRate: 90000,
  18. payloadType: payloadType,
  19. }),
  20. ],
  21. },
  22. iceServers: [
  23. // {
  24. // urls: [
  25. // "stun:caner.top:3478",
  26. // "stun:stun.l.google.com:19302",
  27. // "stun:stun1.l.google.com:19302",
  28. // "stun:stun2.l.google.com:19302",
  29. // "stun:stun3.l.google.com:19302",
  30. // ],
  31. // },
  32. // {
  33. // urls: "turn:caner.top:3478",
  34. // username: "admin",
  35. // credential: "123456",
  36. // },
  37. ]
  38. });
  39. const track = new MediaStreamTrack({ kind: "video" })
  40. randomPort().then((port) => {
  41. const udp = createSocket("udp4");
  42. udp.bind(port);
  43. console.log('端口',port);
  44. // test vp8
  45. /**
  46. * -lavfi 定义一个复杂的过滤器图
  47. */
  48. // exec(`D:/ffmpeg-4.4-full_build/bin/ffmpeg.exe -re -f lavfi -i testsrc=size=640x480:rate=30 -vcodec libvpx -cpu-used 5 -deadline 1 -g 10 -error-resilient 1 -auto-alt-ref 1 -f rtp rtp://127.0.0.1:${port}`,(error, stdout, stderr) => {
  49. // if (error) {
  50. // console.error(`exec error: ${error}`);
  51. // return;
  52. // }
  53. // console.log(`stdout: ${stdout}`);
  54. // console.error(`stderr: ${stderr}`);
  55. // })
  56. // win video h264
  57. /**
  58. * -re 以原生帧速率读取输入
  59. * -f 强制格式
  60. * dshow 直接捕获视频
  61. * -s 视频大小
  62. * -discard 在输入之前丢弃帧(noref:丢弃非参考帧,bidir:丢弃双向帧,nokey:丢弃非关键帧)
  63. * -xerror 出错时停止并退出
  64. * -i 输入
  65. * -pix_fmt 视频格式
  66. * -c 在输入之后是编码,在输入之前是解码
  67. * -vstats 视频日志
  68. * rtp 输出格式
  69. */
  70. exec(`D:/ffmpeg-4.4-full_build/bin/ffmpeg.exe -re -f dshow -s 640x480 -discard nokey -xerror -i video="Logitech HD Webcam C525" -pix_fmt yuv420p -c h264 -vstats -cpu-used 10 -g 5 -preset ultrafast -tune zerolatency -f rtp rtp://127.0.0.1:${port}`, (error, stdout, stderr) => {
  71. if (error) {
  72. console.error(`exec error: ${error}`);
  73. return;
  74. }
  75. })
  76. udp.on("message", (data) => {
  77. const rtp = RtpPacket.deSerialize(data);
  78. rtp.header.payloadType = payloadType;
  79. track.writeRtp(rtp);
  80. });
  81. });
  82. pc.addTransceiver(track, { direction: "sendonly" });
  83. await pc.setLocalDescription(await pc.createOffer());
  84. const sdp = JSON.stringify(pc.localDescription);
  85. socket.send(sdp);
  86. socket.on("message", (data) => {
  87. pc.setRemoteDescription(JSON.parse(data));
  88. });
  89. });