จะห่อ H264 ลงใน FLV ด้วย FFMPEG ได้อย่างไร

ก่อนอื่นคำว่า "เหมาะสม" ในชื่อหมายถึง สิ่งนี้ คำถามที่เกี่ยวข้อง ซึ่งคำตอบไม่สามารถแก้ปัญหาของฉันได้

tl; dr: มีความแตกต่างระหว่างการเข้ารหัสวิดีโอและการจัดเก็บลงใน FLV โดยตรง และดำเนินการในสองขั้นตอนแยกกัน ต้องทำแยกกัน ทำยังไงให้ได้ผลเหมือนทำโดยตรง?

ตัวเข้ารหัสฮาร์ดแวร์ของ Nvidia NVENC จะสร้างข้อมูลดิบ H.264 โดยไม่มีคอนเทนเนอร์ ซึ่งเป็นเรื่องยากที่จะเล่นในเครื่องเล่นวิดีโอส่วนใหญ่ สำหรับแอปพลิเคชัน Adobe AIR ฉันต้องรวมวิดีโอเป็นรูปแบบ FLV ซึ่งฉันต้องการใช้ FFMPEG:

ffmpeg -f h264 -i "input.h264" -c copy -f flv "output.flv"

สิ่งนี้ไม่ได้ผลตามที่คาดไว้ เนื่องจากเฟรมแรกของแต่ละวิดีโอที่ใช้วิธีนี้ไม่ได้แสดงขึ้นมา วิดีโอแต่ละรายการจะแสดงจากเฟรมที่สองเท่านั้น ซึ่งน่าเสียดายสำหรับวิดีโอแบบเฟรมเดียว (ใช้ตัวเข้ารหัสฮาร์ดแวร์ของ GPU สำหรับการบีบอัดภาพที่รวดเร็วปานสายฟ้าเท่านั้น)

สำหรับการตรวจสอบ ตอนนี้ฉันเข้ารหัสวิดีโออินพุตใหม่สองครั้ง: หนึ่งครั้งไปที่เอาต์พุต FLV โดยตรง

ffmpeg -f h264 -i "input.h264" -c:v h264_nvenc -f flv "A.flv"

และอีกครั้งเป็น H.264 จากนั้นจึงผลักมันไปที่ FLV หลังจากนั้น

ffmpeg -f h264 -i "input.h264" -c:v h264_nvenc -f h264 "reencode.h264"
ffmpeg -f h264 -i "reencode.h264" -c copy -f flv "B.flv"

วิดีโอแรกเล่นได้ดี วิดีโอที่สองเล่นไม่ได้ FLV ที่เป็นผลลัพธ์ของวิธีการโดยตรง (A.flv ดูด้านล่าง) มีโครงสร้างไฟล์ที่แตกต่างกันเล็กน้อย โดยเฉพาะอย่างยิ่งหน่วย NAL ที่แตกต่างกัน ซึ่งฉันสงสัยว่าเป็นสาเหตุของพฤติกรรมที่แตกต่างกัน

ดังนั้น คำถามของฉันคือ: หากฉันมีวิดีโอ H.264 อยู่แล้ว และต้องการให้คัดลอกลงในคอนเทนเนอร์ FLV โดยไม่ต้องแปลงรหัส แต่ควรกรอกไฟล์และส่วนหัวของเฟรมอย่างถูกต้องเช่นเดียวกับที่ทำการแปลงรหัสจริง จะทำอย่างไร ฉันบอกสิ่งนี้กับ FFMPEG หรือไม่ มีคำสั่งสำหรับสิ่งนี้ เช่น "-c copy butGenerateValidHeader" หรือไม่

ส่วนที่เกี่ยวข้องของไฟล์มีดังนี้:

วิธีการโดยตรง

ffmpeg -f h264 -i "input.h264" -c:v h264_nvenc -f flv "A.flv"

เอ.flv

46 4C 56 01 01 00 00 00 09 00 00 00 00 12 00 00 // FLV header + metadata
B8 00 00 00 00 00 00 00 02 00 0A 6F 6E 4D 65 74
61 44 61 74 61 08 00 00 00 08 00 08 64 75 72 61
74 69 6F 6E 00 3F A0 E5 60 41 89 37 4C 00 05 77
69 64 74 68 00 40 93 80 00 00 00 00 00 00 06 68
65 69 67 68 74 00 40 8E F0 00 00 00 00 00 00 0D
76 69 64 65 6F 64 61 74 61 72 61 74 65 00 40 9E
84 80 00 00 00 00 00 09 66 72 61 6D 65 72 61 74
65 00 40 3E 00 00 00 00 00 00 00 0C 76 69 64 65
6F 63 6F 64 65 63 69 64 00 40 1C 00 00 00 00 00
00 00 07 65 6E 63 6F 64 65 72 02 00 0D 4C 61 76
66 35 37 2E 37 31 2E 31 30 30 00 08 66 69 6C 65
73 69 7A 65 00 40 F9 5C B0 00 00 00 00 00 00 09

00 00 00 C3 09 00 00 2B 00 00 00 00 00 00 00 17 // AVC sequence start
00 00 00 00
            01 4D 40 20 FF E1 00 17             // ?
                                    67 4D 40 20 // Sequence parameter set
95 A0 13 81 F7 EB 01 10 00 00 3E 80 00 0E A6 08
F1 C3 2A
         01 00 04                               // ?
                  68 EE 3C 80                   // Picture parameter set
                              00 00 00 36 09 01 // AVC NALU
94 9A 00 00 00 00 00 00 00 17 01 00 00 00
                                          00 01 // ?
94 91
      65                                        // IDR frame
        [B8 04 1D FF ...]
00 01 94 A5 09 00 00 05 00 00 00 00 00 00 00    // ?
                                             17 // AVC sequence end
02 00 00 00 00 00 00 10

การเข้ารหัสก่อน

ffmpeg -f h264 -i "input.h264" -c:v h264_nvenc -f h264 "reencode.h264"

เข้ารหัสอีกครั้ง h264

00 00 00 01 67 4D 40 20 95 A0 13 81 F7 EB 01 10 // Sequence parameter set
00 00 3E 80 00 0E A6 08 F1 C3 2A
                                 00 00 00 01 68 // Picture parameter set
EE 3C 80
         00 00 00 01 65                         // IDR frame
                       [B8 04 1D FF ...]        // Frame data

บีบใส่ภาชนะ

ffmpeg -f h264 -i "reencode.h264" -c copy -f flv "B.flv"

บี.flv

46 4C 56 01 01 00 00 00 09 00 00 00 00 12 00 00 // FLV header + metadata
A4 00 00 00 00 00 00 00 02 00 0A 6F 6E 4D 65 74
61 44 61 74 61 08 00 00 00 07 00 08 64 75 72 61
74 69 6F 6E 00 3F A4 7A E1 47 AE 14 7B 00 05 77
69 64 74 68 00 40 93 80 00 00 00 00 00 00 06 68
65 69 67 68 74 00 40 8E F0 00 00 00 00 00 00 0D
76 69 64 65 6F 64 61 74 61 72 61 74 65 00 00 00
00 00 00 00 00 00 00 0C 76 69 64 65 6F 63 6F 64
65 63 69 64 00 40 1C 00 00 00 00 00 00 00 07 65
6E 63 6F 64 65 72 02 00 0D 4C 61 76 66 35 37 2E
37 31 2E 31 30 30 00 08 66 69 6C 65 73 69 7A 65
00 40 F9 5B 40 00 00 00 00 00 00 09
                                    00 00 00 AF // AVC sequence start
09 00 00 05 00 00 00 00 00 00 00 17 00 00 00 00

00 00 00 10 09 01 94 BD 00 00 00 00 00 00 00 17 // AVC NALU
01 00 00
         00 00 00 00 01 67 4D 40 20 95 A0 13 81 // Sequence parameter set
F7 EB 01 10 00 00 3E 80 00 0E A6 08 F1 C3 2A
                                             00 // Picture parameter set
00 00 01 68 EE 3C 80
                     00 00 00 01 65             // IDR frame
                                   [B8 04 1D FF // Frame data
...]
00 01 94 C8 09 00 00 05 00 00 00 00 00 00 00    // ?
                                             17 // AVC sequence end
02 00 00 00 00 00 00 10

อัปเดต 08.08.2017: เพิ่มไฟล์อินพุตและเอาต์พุตสำหรับการตรวจสอบ


person mOfl    schedule 03.08.2017    source แหล่งที่มา
comment
คุณสนใจที่จะแชร์ไฟล์วิดีโอต่างๆ ของคุณหรือไม่?   -  person Markus Schumann    schedule 03.08.2017
comment
ลองใช้บรรทัดคำสั่ง ffmpeg -f h264 -i "reencode.h264" -c copy -bsf:v extract_extradata -f flv "B.flv" เช่น ด้วยตัวกรอง bitstream extract_extradata   -  person nobody555    schedule 03.08.2017
comment
@Markus Schumann: ฉันเพิ่มไฟล์ในตอนท้ายของคำถาม   -  person mOfl    schedule 08.08.2017
comment
@ nobody555: ขอบคุณสำหรับคำแนะนำของคุณ แต่มันใช้งานไม่ได้ วิดีโอไม่เพียงแค่ไม่แสดงใน Adobe AIR เท่านั้น แต่จะไม่แสดงใน MPC-HC อีกต่อไป   -  person mOfl    schedule 08.08.2017
comment
ก่อนอื่น คำอธิบายโครงสร้างไฟล์ของคุณขัดแย้งกับโครงสร้างจริงในไบนารี A.flv และ B.flv ของคุณ (เป็นแบบย้อนกลับ) ผลลัพธ์ใดที่คุณต้องการจริงๆ A.flv (ด้วยรหัสเริ่มต้น: 00 00 00 01 67 .. 00 00 00 01 68 .. 00 00 00 01 65) หรือ B.flv (avcC: 00 17 67 .. 00 04 68 . 00 01 94 91 65)? รูปแบบมาตรฐานสำหรับ FLV จะเหมือนกับ B.flv ที่มี avcC (ไม่มีโค้ดเริ่มต้น) และสร้างขึ้นตามค่าเริ่มต้นด้วย: ffmpeg -i "reencode.h264" -c copy "C.flv": C ฟลอริด้า. หากวิธีนี้ใช้ได้ผลสำหรับคุณให้ลองอัปเดต ffmpeg ของคุณ สำหรับรูปแบบที่มีโค้ดเริ่มต้น ฉันไม่รู้ว่าจะสร้างมันขึ้นมาได้อย่างไร   -  person nobody555    schedule 08.08.2017
comment
@ nobody555 ได้โปรดกรุณาตอบสิ่งนี้และขอขอบคุณที่ให้ไฟล์นั้น ฉันรู้ว่าไฟล์ของคุณมี Lavf57.76.100 เป็นตัวเข้ารหัส ในขณะที่ของฉันมี Lavf57.71.100 ดังนั้นฉันจึงอัปเดต FFMPEG จาก 3.3.2 เป็น 3.3.3 (สร้างทุกคืน 20170807-1bef008) และตอนนี้ FLV ที่ muxed จะเล่นใน AIR ตามต้องการ   -  person mOfl    schedule 09.08.2017
comment
@mOfl โซลูชันนี้ยังต้องการอินพุต h.264 เพื่อเข้ารหัสใหม่ครั้งแรกเป็น h.264 วินาทีใหม่หรือไม่ ถ้าอย่างนั้น ใหม่ h.264 (reencoded.h264) จะถูกบรรจุเป็น FLV หรือไม่ หากคุณไม่สนใจที่จะข้าม FFmpeg ไปเลย ฉันจะแสดงโค้ด AS3 ที่แสดง input.h264 นั้นโดยตรงภายในแอป AIR มีเฉพาะภาพนิ่ง h264 เท่านั้นหรือเปล่าครับ?   -  person VC.One    schedule 09.08.2017
comment
@ VC.One ไม่ input.h264 ไม่จำเป็นต้องเข้ารหัสใหม่ ฉันเพิ่งทำสิ่งนี้เพื่อดูความแตกต่างในไฟล์ที่ FFMPEG สร้างขึ้น ฉันอยากจะข้าม FFMPEG และ FLV หากมีวิธีการเล่นภาพนิ่ง h264 โดยตรง (ในรูปแบบภาคผนวก B) ใน AIR!   -  person mOfl    schedule 10.08.2017


คำตอบ (2)


ก่อนอื่น คำอธิบายโครงสร้างไฟล์ของคุณขัดแย้งกับโครงสร้างจริงในไบนารี A.flv และ B.flv ของคุณ (เป็นแบบย้อนกลับ) แล้วคุณต้องการผลลัพธ์แบบไหนล่ะ? ชอบ A.flv ที่มีรหัสเริ่มต้น: 00 00 00 01 67 .. 00 00 00 01 68 .. 00 00 00 01 65 หรือชอบ B.flv ที่มี avcC + 00 17 67 .. 00 04 68 .. 00 01 94 91 65?

รูปแบบมาตรฐานสำหรับ FLV จะเหมือนกับ B.flv ที่มี avcC (ไม่มีโค้ดเริ่มต้น) และสร้างขึ้นตามค่าเริ่มต้นด้วย: ffmpeg -i "reencode.h264" -c copy "C.flv": ผลลัพธ์ที่ได้คือ ซี.flv.

หากไฟล์นี้เหมาะกับคุณมากกว่าให้ลองอัปเดต ffmpeg ของคุณ (ฉันใช้เวอร์ชันที่คอมไพล์เองจากสาขาหลัก) สำหรับรูปแบบที่มีโค้ดเริ่มต้น ฉันไม่รู้ว่าจะสร้างมันขึ้นมาได้อย่างไร

person nobody555    schedule 09.08.2017

(1)

"หากฉันมีวิดีโอ H.264 อยู่แล้วและต้องการให้คัดลอกลงในคอนเทนเนอร์ FLV โดยไม่ต้องแปลงรหัส แต่ควรกรอกไฟล์และส่วนหัวของเฟรมอย่างถูกต้องเช่นเดียวกับที่ทำเมื่อ จริงๆ แล้วการแปลงรหัส ฉันจะบอกเรื่องนี้กับ FFMPEG ได้อย่างไร"

คุณต้องการ muxing : ลองใช้คำสั่งด้านล่าง (คัดลอกข้อมูล H.264 โดยตรงลงในคอนเทนเนอร์ FLV)

ffmpeg -i input.h264 -c:v copy output.flv

(2)

เฟรมแรกของแต่ละวิดีโอที่ใช้วิธีนี้จะไม่แสดง วิดีโอแต่ละรายการจะแสดงจากเฟรมที่สองเท่านั้น ซึ่งน่าเสียดายสำหรับวิดีโอแบบเฟรมเดียว...

คุณจะตรวจสอบเฟรมอย่างไรเพื่อให้รู้ว่ามันแสดงเฟรม 2 เป็นต้นไปเท่านั้น เป็นตามรหัส AS3 netStream.pause() หรือโดยเครื่องเล่นสื่อเช่น VLC

ใน FLV แต่ละเฟรมวิดีโอจะเข้าสู่ Video Tag สำหรับตัวแปลงสัญญาณอื่นๆ เราสามารถพูดว่า ใส่ frame #1 ลงใน videoTag #1 ฯลฯ แต่สำหรับ H.264 แท็กแรกจะเก็บข้อมูล "การกำหนดค่าตัวถอดรหัส AVC" ไว้เสมอ ดังนั้นพิกเซล ของเฟรมวิดีโออินพุต #1 จะมีอยู่ที่ videoTag #2 ของ FLV
นอกจากนี้ ด้วยไบต์แรกของคุณที่แสดง... ในส่วน "AVC NALU" ที่ 17 01 แจ้งให้คุณทราบว่านี่คือคีย์เฟรม (เฟรมแรกจะเป็นคีย์เฟรมเสมอ) ตัวถอดรหัส MPEG จะไม่แสดงเฟรมที่สอง (โดยปกติคือ P-frame) หากไม่ได้อยู่ที่ คีย์เฟรมถอดรหัสครั้งแรก (I-frame) รูปภาพเฟรม #1 ของคุณมีอยู่ที่ไหนสักแห่ง...

ฉันไม่สามารถสร้างปัญหานี้ขึ้นใหม่ด้วยวิดีโอ H.264 ที่มีการนับถอยหลังตามรหัสเวลาบางรายการ เฟรมแรกเริ่มต้นที่ 00hh:00mm:00ss:00msec และมิลลิวินาทีจะเลื่อนขึ้นทีละเฟรม หลังจากใช้คำสั่งที่แสดงในจุด (1) ฉันเห็นเฟรมแรกใน VLC & MPC-HC และใน AS3 ด้วย ถ้าฉันใช้ appendBytes เพื่อป้อนตัวถอดรหัสด้วยข้อมูลของเฟรมเดียว (byteArray จัดทำเป็น FLV 1 เฟรม)

person VC.One    schedule 06.08.2017
comment
น่าเสียดายที่การรีมิกซ์ตามที่แนะนำไม่ได้ผล ตรวจสอบคำถามที่อัปเดตของฉันเพื่อดูวิดีโอผลลัพธ์ (copy.flv) ขณะที่เล่นใน MPC-HC จะไม่แสดงใน AIR (AS3 โดยใช้ netStream.appendBytes) - person mOfl; 08.08.2017