diff --git a/ffmpegutil/ffmpeg.go b/ffmpegutil/ffmpeg.go index b83dff8..73eef44 100644 --- a/ffmpegutil/ffmpeg.go +++ b/ffmpegutil/ffmpeg.go @@ -56,7 +56,18 @@ func MuxAudioAndVideo(partition *ubv.UbvPartition, h264File string, aacFile stri videoTrack.Rate = 1 } - cmd := exec.Command(getFfmpegCommand(), "-i", h264File, "-itsoffset", strconv.FormatFloat(audioDelaySec, 'f', -1, 32), "-i", aacFile, "-map", "0:v", "-map", "1:a", "-c", "copy", "-r", strconv.Itoa(videoTrack.Rate), "-y", "-loglevel", "warning", mp4File) + cmd := exec.Command(getFfmpegCommand(), + "-i", h264File, + "-itsoffset", strconv.FormatFloat(audioDelaySec, 'f', -1, 32), + "-i", aacFile, + "-map", "0:v", + "-map", "1:a", + "-c", "copy", + "-r", strconv.Itoa(videoTrack.Rate), + "-timecode", ubv.GenerateTimecode(videoTrack.StartTimecode, videoTrack.Rate), + "-y", + "-loglevel", "warning", + mp4File) runFFmpeg(cmd) } diff --git a/ubv/ubvfile.go b/ubv/ubvfile.go index 2a2ee7b..3b0a45e 100644 --- a/ubv/ubvfile.go +++ b/ubv/ubvfile.go @@ -4,6 +4,7 @@ import ( "log" "strconv" "time" + "fmt" ) const ( @@ -153,3 +154,24 @@ func guessVideoRate(durations [32]int) int { return mostFrequent } + +/** + * Generates a timecode string from a StartTimecode object and framerate. + * The timecode is set as the wall clock time (so a clip starting at 03:45 pm and 13 seconds will have a timestamp of 03:45:13) + * Additionally, the nanosecond time value is rounded to the nearest frame index based on the framerate, + * so a 13.50000 second time is frame 16 on a 30 fps clip (frames are indexed from 1 onwards). + * So the clip will have a full timestamp of 03:34:13.16 + * + * @param startTimecode The StartTimecode object to generate a timecode string from + * @param framerate The framerate of the video + * @return The timecode string + */ + func GenerateTimecode(startTimecode time.Time, framerate int) string { + + var timecode string + // calculate timecode ( HH:MM:SS.FF ) from seconds and nanoseconds for frame part + timecode = startTimecode.Format("15:04:05") + "." + fmt.Sprintf("%02.0f", ((float32(startTimecode.Nanosecond()) / float32(1000000000.0) * float32(framerate)) + 1) ) + // log.Println("Timecode: ", timecode) + // log.Printf("Date/Time: %s", videoTrack.StartTimecode) + return timecode +} diff --git a/ubvfile_test.go b/ubvfile_test.go index 365beb7..bc9c0a5 100644 --- a/ubvfile_test.go +++ b/ubvfile_test.go @@ -7,6 +7,14 @@ import ( "ubvremux/ubv" ) +func TestGenerateTimecode(t *testing.T) { + timecode := ubv.GenerateTimecode(time.Date(2023, time.Month(5), 16, 11, 58, 26, 500000000, time.UTC), 30) + log.Printf("Timecode Generated") + if timecode != "11:58:26.16" { + t.Errorf("Timecode generated is incorrect, got: %s, want: %s.", timecode, "11:58:26.16") + } +} + func TestCopyFrames(t *testing.T) { ubvFile := "samples/FCECDA1F0A63_0_rotating_1597425468956.ubv"