Files
monibuca/plugin/mp4/pkg/muxer_test.go
langhuihui 8a9fffb987 refactor: frame converter and mp4 track improvements
- Refactor frame converter implementation
- Update mp4 track to use ICodex
- General refactoring and code improvements

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-28 19:55:37 +08:00

163 lines
4.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package mp4
import (
"fmt"
"io"
"os"
"os/exec"
"testing"
"m7s.live/v5/plugin/mp4/pkg/box"
)
func TestFLVToMP4(t *testing.T) {
// Open FLV file
flvFile, err := os.Open("/Users/dexter/Movies/frame_counter_4k_60fps.flv")
if err != nil {
t.Fatalf("Failed to open FLV file: %v", err)
}
defer flvFile.Close()
// Create output MP4 file
outFile, err := os.Create("test_regular.mp4")
if err != nil {
t.Fatalf("Failed to create output file: %v", err)
}
defer outFile.Close()
// Create regular MP4 muxer (without fragmentation flag)
muxer := NewMuxer(0) // No flags for regular MP4
muxer.WriteInitSegment(outFile)
// Read FLV header
header, err := readFLVHeader(flvFile)
if err != nil {
t.Fatalf("Failed to read FLV header: %v", err)
}
hasVideo := header.Flags&0x01 != 0
hasAudio := header.Flags&0x04 != 0
// Skip to the first tag
if _, err := flvFile.Seek(int64(header.DataOffset), io.SeekStart); err != nil {
t.Fatalf("Failed to seek to first tag: %v", err)
}
// Create tracks
var videoTrack, audioTrack *Track
if hasVideo {
videoTrack = muxer.AddTrack(box.MP4_CODEC_H264)
videoTrack.Timescale = 1000
}
if hasAudio {
audioTrack = muxer.AddTrack(box.MP4_CODEC_AAC)
audioTrack.Timescale = 1000
}
// Variables to store codec configuration
var videoConfig, audioConfig []byte
var frameCount, sampleCount int
// Process FLV tags
for {
tag, err := readFLVTag(flvFile)
if err != nil {
if err == io.EOF {
break
}
t.Fatalf("Failed to read FLV tag: %v", err)
}
switch tag.TagType {
case 8: // Audio
if !hasAudio || audioTrack == nil {
continue
}
soundFormat := tag.Data[0] >> 4
if soundFormat != 10 { // AAC
continue
}
aacPacketType := tag.Data[1]
if aacPacketType == 0 { // AAC sequence header
fmt.Println("Found AAC sequence header")
audioConfig = tag.Data[2:] // Store AAC config
// 这里应该创建 AAC codec context但为了简化测试我们暂时跳过
// TODO: 创建适当的 AAC codec context
} else if len(audioConfig) > 0 { // Audio data
if len(tag.Data) <= 2 {
fmt.Printf("Skipping empty audio sample at timestamp %d\n", tag.Timestamp)
continue
}
sample := box.Sample{
Timestamp: uint32(tag.Timestamp),
CTS: 0,
KeyFrame: true, // Audio samples are always key frames
}
sample.PushOne(tag.Data[2:])
if err := muxer.WriteSample(outFile, audioTrack, sample); err != nil {
t.Fatalf("Failed to write audio sample: %v", err)
}
sampleCount++
}
case 9: // Video
if !hasVideo || videoTrack == nil {
continue
}
codecID := tag.Data[0] & 0x0f
frameType := tag.Data[0] >> 4
if codecID == 7 { // AVC/H.264
if tag.Data[1] == 0 { // AVC sequence header
fmt.Println("Found AVC sequence header")
videoConfig = tag.Data[5:] // Store AVC config (skip composition time)
// 这里应该创建 H264 codec context但为了简化测试我们暂时跳过
// TODO: 创建适当的 H264 codec context
} else if len(videoConfig) > 0 { // Video data
if len(tag.Data) <= 5 {
fmt.Printf("Skipping empty video sample at timestamp %d\n", tag.Timestamp)
continue
}
// Read composition time offset (24 bits, signed)
compositionTime := int32(tag.Data[2])<<16 | int32(tag.Data[3])<<8 | int32(tag.Data[4])
if compositionTime&0x800000 != 0 {
compositionTime |= ^0xffffff
}
sample := box.Sample{
Timestamp: uint32(tag.Timestamp),
CTS: uint32(compositionTime),
KeyFrame: frameType == 1,
}
sample.PushOne(tag.Data[5:])
if err := muxer.WriteSample(outFile, videoTrack, sample); err != nil {
t.Fatalf("Failed to write video sample: %v", err)
}
frameCount++
}
}
}
}
// Write trailer
if err := muxer.WriteTrailer(outFile); err != nil {
t.Fatalf("Failed to write trailer: %v", err)
}
fmt.Printf("Conversion completed successfully with %d video frames and %d audio samples\n", frameCount, sampleCount)
// Validate the generated MP4 file using MP4Box
cmd := exec.Command("MP4Box", "-info", "test_regular.mp4")
output, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("MP4Box validation failed: %v\nOutput: %s", err, output)
}
fmt.Printf("MP4Box validation output:\n%s\n", output)
t.Log("Test completed successfully")
}