ฉันพยายามอย่างยิ่งที่จะเข้าใจวิธีที่ดีที่สุดในการส่งกระแสข้อมูลเรียลไทม์ของ ffmpeg ไปยังไคลเอนต์ HTML5 โดยใช้ node.js เนื่องจากมีจำนวนของตัวแปรที่เล่นและฉันไม่ได้มีประสบการณ์มากมายในพื้นที่นี้ ต้องใช้เวลาหลายชั่วโมงในการลองชุดค่าผสมที่แตกต่างกัน
กรณีใช้ของฉันคือ:
1) กล้องวิดีโอ IP IP สตรีม RTSP H.264 ถูกหยิบขึ้นมาโดย FFMPEG และนำกลับเข้าไปในคอนเทนเนอร์ mp4 โดยใช้การตั้งค่า FFMPEG ต่อไปนี้ในโหนดส่งออกไปยัง STDOUT นี่จะทำงานเฉพาะในการเชื่อมต่อไคลเอนต์เริ่มต้นดังนั้นคำขอเนื้อหาบางส่วนจะไม่พยายามวางไข่ FFMPEG อีกครั้ง
liveFFMPEG = child_process.spawn("ffmpeg", [
"-i", "rtsp://admin:12345@192.168.1.234:554" , "-vcodec", "copy", "-f",
"mp4", "-reset_timestamps", "1", "-movflags", "frag_keyframe+empty_moov",
"-" // output to stdout
], {detached: false});
2) ฉันใช้เซิร์ฟเวอร์ http http เพื่อจับ STDOUT และสตรีมที่กลับไปยังไคลเอนต์ตามคำขอของลูกค้า เมื่อไคลเอนต์แรกเชื่อมต่อฉันวางไข่บรรทัดคำสั่ง FFMPEG ข้างต้นแล้วไพพ์สตรีม STDOUT ไปยังการตอบสนอง HTTP
liveFFMPEG.stdout.pipe(resp);
ฉันยังใช้เหตุการณ์สตรีมเพื่อเขียนข้อมูล FFMPEG ไปยังการตอบกลับ HTTP แต่ไม่ได้ทำให้แตกต่าง
xliveFFMPEG.stdout.on("data",function(data) {
resp.write(data);
}
ฉันใช้ส่วนหัว HTTP ต่อไปนี้ (ซึ่งใช้และทำงานเมื่อสตรีมไฟล์ที่บันทึกไว้ล่วงหน้า)
var total = 999999999 // fake a large file
var partialstart = 0
var partialend = total - 1
if (range !== undefined) {
var parts = range.replace(/bytes=/, "").split("-");
var partialstart = parts[0];
var partialend = parts[1];
}
var start = parseInt(partialstart, 10);
var end = partialend ? parseInt(partialend, 10) : total; // fake a large file if no range reques
var chunksize = (end-start)+1;
resp.writeHead(206, {
'Transfer-Encoding': 'chunked'
, 'Content-Type': 'video/mp4'
, 'Content-Length': chunksize // large size to fake a file
, 'Accept-Ranges': 'bytes ' + start + "-" + end + "/" + total
});
3) ลูกค้าต้องใช้แท็กวิดีโอ HTML5
ฉันไม่มีปัญหากับการเล่นแบบสตรีมมิ่ง (ใช้ fs.createReadStream with 206 HTTP เนื้อหาบางส่วน) ไปยังไคลเอนต์ HTML5 ไฟล์วิดีโอที่บันทึกไว้ก่อนหน้านี้ด้วยบรรทัดคำสั่ง FFMPEG ข้างต้น (แต่บันทึกเป็นไฟล์แทน STDOUT) ดังนั้นฉันจึงรู้ FFMPEG สตรีม ถูกต้องและฉันยังสามารถดูสตรีมมิ่งวิดีโอสดใน VLC ได้อย่างถูกต้องเมื่อเชื่อมต่อกับเซิร์ฟเวอร์โหนด HTTP
อย่างไรก็ตามการพยายามสตรีมสดจาก FFMPEG ผ่านทางโหนด HTTP ดูเหมือนว่าจะยากขึ้นมากเนื่องจากไคลเอนต์จะแสดงหนึ่งเฟรมจากนั้นหยุด ฉันสงสัยว่าปัญหาคือฉันไม่ได้ตั้งค่าการเชื่อมต่อ HTTP ให้เข้ากันได้กับไคลเอนต์วิดีโอ HTML5 ฉันได้ลองทำสิ่งต่าง ๆ เช่นการใช้ HTTP 206 (เนื้อหาบางส่วน) และการตอบสนอง 200 ครั้งการใส่ข้อมูลลงในบัฟเฟอร์จากนั้นสตรีมมิ่งโดยไม่มีโชคดังนั้นฉันต้องย้อนกลับไปที่หลักการแรกเพื่อให้แน่ใจว่าฉันตั้งค่าถูกต้องแล้ว ทาง
นี่คือความเข้าใจของฉันเกี่ยวกับวิธีการทำงานนี้โปรดแก้ไขฉันหากฉันผิด:
1) FFMPEG ควรตั้งค่าให้แฟรกเมนต์เอาต์พุตและใช้ moov ว่างเปล่า (แฟล็ก FFMPEG Frag_keyframe และค่าสถานะ mov mov_moov) ซึ่งหมายความว่าลูกค้าไม่ได้ใช้ moov atom ซึ่งโดยทั่วไปแล้วจะอยู่ที่ท้ายไฟล์ซึ่งไม่เกี่ยวข้องเมื่อทำการสตรีม (ไม่สิ้นสุดไฟล์) แต่หมายถึงไม่สามารถค้นหาสิ่งที่เป็นไปได้ซึ่งเหมาะสำหรับเคสการใช้งานของฉัน
2) แม้ว่าฉันจะใช้ชิ้นส่วน MP4 และ MOOV ที่ว่างเปล่าฉันยังคงต้องใช้เนื้อหา HTTP บางส่วนเนื่องจากผู้เล่น HTML5 จะรอจนกว่าจะดาวน์โหลดสตรีมทั้งหมดก่อนที่จะเล่นซึ่งการสตรีมสดจะไม่สิ้นสุด
3) ฉันไม่เข้าใจว่าทำไมการไพพ์สตรีม STDOUT ไปยังการตอบสนอง HTTP ไม่ทำงานเมื่อสตรีมมิงแบบสด แต่ถ้าฉันบันทึกเป็นไฟล์ฉันสามารถสตรีมไฟล์นี้ไปยังไคลเอนต์ HTML5 ได้อย่างง่ายดายโดยใช้รหัสที่คล้ายกัน อาจเป็นปัญหาเกี่ยวกับเวลาเนื่องจากใช้เวลาวินาทีสำหรับ FFMPEG วางไข่เริ่มต้นเชื่อมต่อกับกล้อง IP และส่งชิ้นไปยังโหนดและเหตุการณ์ข้อมูลโหนดก็ผิดปกติเช่นกัน อย่างไรก็ตาม bytestream ควรเหมือนกับการบันทึกไฟล์และ HTTP ควรจะรองรับความล่าช้าได้
4) เมื่อตรวจสอบบันทึกเครือข่ายจากไคลเอนต์ HTTP เมื่อสตรีมไฟล์ MP4 ที่สร้างโดย FFMPEG จากกล้องฉันเห็นว่ามีคำขอไคลเอนต์ 3 คำขอ: คำขอ GET ทั่วไปสำหรับวิดีโอซึ่งเซิร์ฟเวอร์ HTTP ส่งคืนประมาณ 40Kb จากนั้นบางส่วน คำขอเนื้อหาที่มีช่วงไบต์สำหรับ 10K สุดท้ายของไฟล์จากนั้นคำขอสุดท้ายสำหรับบิตที่อยู่ตรงกลางจะไม่โหลด บางทีไคลเอนต์ HTML5 เมื่อได้รับการตอบกลับครั้งแรกขอให้ส่วนสุดท้ายของไฟล์โหลด atom MOOV MP4 หรือไม่ หากเป็นกรณีนี้มันจะไม่ทำงานสำหรับการสตรีมเนื่องจากไม่มีไฟล์ MOOV และไม่มีจุดสิ้นสุดไฟล์
5) เมื่อตรวจสอบบันทึกเครือข่ายเมื่อพยายามสตรีมสดฉันได้รับคำขอเริ่มต้นที่ถูกยกเลิกโดยได้รับประมาณ 200 ไบต์เท่านั้นจากนั้นคำขอซ้ำอีกครั้งจะถูกยกเลิกด้วย 200 ไบต์และคำขอที่สามซึ่งมีความยาวเพียง 2K เท่านั้น ฉันไม่เข้าใจว่าทำไมไคลเอนต์ HTML5 จะยกเลิกการร้องขอเนื่องจาก bytestream เหมือนกับที่ฉันสามารถใช้งานได้สำเร็จเมื่อสตรีมจากไฟล์ที่บันทึกไว้ ดูเหมือนว่าโหนดไม่ได้ส่งสตรีม FFMPEG ที่เหลือไปยังไคลเอนต์ แต่ฉันสามารถดูข้อมูล FFMPEG ในรูทีนเหตุการณ์. on รูทีนเพื่อให้มันไปยังเซิร์ฟเวอร์ HTTP HTTP โหนด FFMPEG
6) แม้ว่าฉันคิดว่าการไพพ์สตรีม STDOUT ไปยังบัฟเฟอร์การตอบสนอง HTTP ควรใช้งานได้หรือไม่ฉันต้องสร้างบัฟเฟอร์ระดับกลางและสตรีมที่อนุญาตให้ไคลเอนต์เนื้อหา HTTP บางส่วนทำงานได้อย่างถูกต้องเหมือนที่มันทำเมื่ออ่านไฟล์สำเร็จ ? ฉันคิดว่านี่เป็นเหตุผลหลักสำหรับปัญหาของฉัน แต่ฉันไม่แน่ใจว่าในโหนดวิธีการตั้งค่าที่ดีที่สุด และฉันไม่รู้วิธีจัดการกับคำขอของลูกค้าสำหรับข้อมูลที่ส่วนท้ายของไฟล์เนื่องจากไม่มีจุดสิ้นสุดไฟล์
7) ฉันอยู่ผิดทางโดยพยายามจัดการคำขอเนื้อหาบางส่วน 206 รายการและควรทำงานกับการตอบกลับ HTTP 200 ปกติหรือไม่ การตอบสนอง HTTP 200 ทำงานได้ดีสำหรับ VLC ดังนั้นฉันสงสัยว่าไคลเอนต์วิดีโอ HTML5 จะทำงานกับคำขอเนื้อหาบางส่วนเท่านั้น
ในขณะที่ฉันยังคงเรียนรู้สิ่งนี้มันยากที่จะทำงานผ่านเลเยอร์ต่าง ๆ ของปัญหานี้ (FFMPEG, โหนด, สตรีมมิ่ง, HTTP, วิดีโอ HTML5) เพื่อให้พอยน์เตอร์ใด ๆ ได้รับการชื่นชมอย่างมาก ฉันใช้เวลาหลายชั่วโมงทำการค้นคว้าในเว็บไซต์นี้และในเน็ตและฉันไม่เคยเจอใครที่สามารถทำการสตรีมมิ่งแบบเรียลไทม์ในโหนด แต่ฉันไม่สามารถเป็นคนแรกและฉันคิดว่ามันควรจะทำงานได้ !)
Content-Type
หัวของคุณหรือไม่? คุณใช้การเข้ารหัสก้อนหรือไม่? นั่นคือสิ่งที่ฉันจะเริ่มต้น นอกจากนี้ HTML5 ไม่จำเป็นต้องให้การทำงานเพื่อสตรีมคุณสามารถอ่านเพิ่มเติมเกี่ยวกับที่นี่ คุณจะต้องใช้วิธีการบัฟเฟอร์และเล่นสตรีมวิดีโอโดยใช้วิธีการของคุณเอง ( ดูที่นี่ ) คิดว่าสิ่งนี้อาจไม่ได้รับการสนับสนุนอย่างดี นอกจากนี้ google ใน MediaSource API