Skip to content

Commit

Permalink
Remove .aac extraction and FFmpeg muxing, only emit .h264 bitstreams
Browse files Browse the repository at this point in the history
  • Loading branch information
petergeneric committed Aug 16, 2023
1 parent 82f4d67 commit 6dfaebe
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 234 deletions.
43 changes: 3 additions & 40 deletions demux/demux.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"ubvremux/ubv"
)

func DemuxSinglePartitionToNewFiles(ubvFilename string, videoFilename string, audioFilename string, partition *ubv.UbvPartition) {
func DemuxSinglePartitionToNewFiles(ubvFilename string, videoFilename string, partition *ubv.UbvPartition) {

// The input media file; N.B. we do not use a buffered reader for this because we will be seeking heavily
ubvFile, err := os.OpenFile(ubvFilename, os.O_RDONLY, 0)
Expand All @@ -33,26 +33,11 @@ func DemuxSinglePartitionToNewFiles(ubvFilename string, videoFilename string, au
videoFile = nil
}

// Optionally write audio
var audioFile *bufio.Writer
if len(audioFilename) > 0 && partition.AudioTrackCount > 0 {
audioFileRaw, err := os.Create(audioFilename)
if err != nil {
log.Fatal("Error opening audio bitstream output", err)
}

defer audioFileRaw.Close()

audioFile = bufio.NewWriter(audioFileRaw)
} else {
audioFile = nil
}

DemuxSinglePartition(ubvFilename, partition, videoFile, ubvFile, audioFile)
DemuxSinglePartition(ubvFilename, partition, videoFile, ubvFile)
}

// Extract video and audio data from a given partition of a .ubv file into raw .H264 bitstream and/or raw .AAC bitstream file
func DemuxSinglePartition(ubvFilename string, partition *ubv.UbvPartition, videoFile *bufio.Writer, ubvFile *os.File, audioFile *bufio.Writer) {
func DemuxSinglePartition(ubvFilename string, partition *ubv.UbvPartition, videoFile *bufio.Writer, ubvFile *os.File) {
// Allocate a buffer large enough for the largest frame
var buffer []byte
{
Expand Down Expand Up @@ -113,34 +98,12 @@ func DemuxSinglePartition(ubvFilename string, partition *ubv.UbvPartition, video
log.Fatal("Failed to write output NAL Separator! Only wrote ", bytesWritten, " bytes. Error:", err)
}
}

} else if frame.TrackNumber == 1000 && audioFile != nil {
// Audio packet - contains raw AAC bitstream

// Seek
if _, err := ubvFile.Seek(int64(frame.Offset), io.SeekStart); err != nil {
log.Fatal("Failed to seek to ", frame.Offset, "in ", ubvFilename, err)
}

// Read
if _, err := io.ReadFull(ubvFile, buffer[0:frame.Size]); err != nil {
log.Fatal("Failed to read ", frame.Size, " bytes at ", frame.Offset, err)
}

if bytesWritten, err := audioFile.Write(buffer[0:frame.Size]); err != nil {
log.Fatal("Failed to write output audio data! Only wrote ", bytesWritten, ". Error:", err)
}
} else {
continue
}
}

// Flush all buffered output data

if audioFile != nil {
audioFile.Flush()
}

if videoFile != nil {
videoFile.Flush()
}
Expand Down
115 changes: 0 additions & 115 deletions ffmpegutil/ffmpeg.go

This file was deleted.

98 changes: 19 additions & 79 deletions remux.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"strings"
"time"
"ubvremux/demux"
"ubvremux/ffmpegutil"
"ubvremux/ubv"
)

Expand All @@ -20,79 +19,44 @@ var GitCommit string

// Parses and validates commandline options and passes them to RemuxCLI
func main() {
includeAudioPtr := flag.Bool("with-audio", false, "If true, extract audio")
includeVideoPtr := flag.Bool("with-video", true, "If true, extract video")
forceRatePtr := flag.Int("force-rate", 0, "If non-zero, adds a -r argument to FFmpeg invocations")
outputFolder := flag.String("output-folder", "./", "The path to output remuxed files to. \"SRC-FOLDER\" to put alongside .ubv files")
remuxPtr := flag.Bool("mp4", true, "If true, will create an MP4 as output")
outputFolder := flag.String("output-folder", "./", "The path to output demuxed files to. \"SRC-FOLDER\" to put alongside .ubv files")
versionPtr := flag.Bool("version", false, "Display version and quit")

flag.Parse()

// Perform some argument combo validation
if *versionPtr {
println("UBV Remux Tool")
println("Copyright (c) Peter Wright 2020-2021")
println("UBV Demux Tool")
println("Copyright (c) Peter Wright 2020-2023")
println("https://github.com/petergeneric/unifi-protect-remux")
println("")

// If there's a release version specified, use that. Otherwise print the git revision
if len(ReleaseVersion) > 0 {
println("\tVersion: ", ReleaseVersion)
} else {
println("\tGit commit: ", GitCommit)
}
println("\tVersion: (custom build 1)")
println("\tGit commit: ", GitCommit)

os.Exit(0)
} else if len(flag.Args()) == 0 {
// Terminate immediately if no .ubv files were provided
println("Expected at least one .ubv file as input!\n")

flag.Usage()
os.Exit(1)
} else if !*includeAudioPtr && !*includeVideoPtr {
// Fail if extracting neither audio nor video
println("Must enable extraction of at least one of: audio, video!\n")

flag.Usage()
os.Exit(1)
}

RemuxCLI(flag.Args(), *includeAudioPtr, *includeVideoPtr, *forceRatePtr, *remuxPtr, *outputFolder)
RemuxCLI(flag.Args(), *outputFolder)
}

// Takes parsed commandline args and performs the remux tasks across the set of input files
func RemuxCLI(files []string, extractAudio bool, extractVideo bool, forceRate int, createMP4 bool, outputFolder string) {
// RemuxCLI Takes parsed commandline args and performs the remux tasks across the set of input files
func RemuxCLI(files []string, outputFolder string) {
for _, ubvFile := range files {
log.Println("Analysing ", ubvFile)
info := ubv.Analyse(ubvFile, extractAudio)

log.Printf("\n\nAnalysis complete!\n")
if len(info.Partitions) > 0 {
log.Printf("First Partition:")
log.Printf("\tTracks: %d", len(info.Partitions[0].Tracks))
log.Printf("\tFrames: %d", len(info.Partitions[0].Frames))
log.Printf("\tStart Timecode: %s", info.Partitions[0].Tracks[7].StartTimecode.Format(time.RFC3339))
}
info := ubv.Analyse(ubvFile, false)

log.Printf("\n\nExtracting %d partitions", len(info.Partitions))
log.Printf("\n\nAnalysis complete!")
log.Printf("Extracting %d partitions...\n", len(info.Partitions))

// Optionally apply the user's forced framerate
if forceRate > 0 {
log.Println("\nFramerate forced by user instruction: using ", forceRate, " fps")
for _, partition := range info.Partitions {
for _, track := range partition.Tracks {
if track.IsVideo {
track.Rate = forceRate
}
}
}
}

for _, partition := range info.Partitions {
for i, partition := range info.Partitions {
var videoFile string
var audioFile string
var mp4 string
{
outputFolder := strings.TrimSuffix(outputFolder, "/")

Expand All @@ -111,40 +75,16 @@ func RemuxCLI(files []string, extractAudio bool, extractVideo bool, forceRate in

basename := outputFolder + "/" + baseFilename + "_" + strings.ReplaceAll(getStartTimecode(partition).Format(time.RFC3339), ":", ".")

if extractVideo && partition.VideoTrackCount > 0 {
videoFile = basename + ".h264"
}

if extractAudio && partition.AudioTrackCount > 0 {
audioFile = basename + ".aac"
}

if createMP4 {
mp4 = basename + ".mp4"
}
videoFile = basename + ".h264"
}

demux.DemuxSinglePartitionToNewFiles(ubvFile, videoFile, audioFile, partition)

if createMP4 {
log.Println("\nWriting MP4 ", mp4, "...")
log.Printf("Partition %d:", i)
log.Printf("\tTracks: %d", len(partition.Tracks))
log.Printf("\tFrames: %d", len(partition.Frames))
log.Printf("\tStart Timecode: %s", partition.Tracks[7].StartTimecode.Format(time.RFC3339))
log.Printf("\tOutput File: %s\n", videoFile)

// Spawn FFmpeg to remux
// TODO: could we generate an MP4 directly? Would require some analysis of the input bitstreams to build MOOV
ffmpegutil.MuxAudioAndVideo(partition, videoFile, audioFile, mp4)

// Delete
if len(videoFile) > 0 {
if err := os.Remove(videoFile); err != nil {
log.Println("Warning: could not delete ", videoFile+": ", err)
}
}
if len(audioFile) > 0 {
if err := os.Remove(audioFile); err != nil {
log.Println("Warning: could not delete ", audioFile+": ", err)
}
}
}
demux.DemuxSinglePartitionToNewFiles(ubvFile, videoFile, partition)
}
}
}
Expand Down

0 comments on commit 6dfaebe

Please sign in to comment.