Skip to content

Instantly share code, notes, and snippets.

@jeromecornet
Created December 6, 2023 18:21
Show Gist options
  • Save jeromecornet/7477bbd9e8e8ada14b1eb8ef358b2ffc to your computer and use it in GitHub Desktop.
Save jeromecornet/7477bbd9e8e8ada14b1eb8ef358b2ffc to your computer and use it in GitHub Desktop.
Davinci Resolve Studio import HiLight tags from GoPro footage as a clip marker. Credit for actual tag parsing goes to https://github.com/icegoogles/GoPro-Highlight-Parser
#!/usr/bin/env python
"""
Script to import GoPro HiLight tags as clip markers.
Execute this in the bin where your footage is stored, it will extract the highlight tags
"""
import os
import sys
import struct
sys.path.append("/Library/Application Support/Blackmagic Design/DaVinci Resolve/Developer/Scripting/Modules")
import DaVinciResolveScript as dvr_script
from math import floor
resolve = dvr_script.scriptapp("Resolve")
project = resolve.GetProjectManager().GetCurrentProject()
if not project:
print("No project is loaded")
sys.exit()
resolve.OpenPage("media")
projectManager = resolve.GetProjectManager()
mediaPool = project.GetMediaPool()
currentBin = mediaPool.GetCurrentFolder()
# Gets clips
clips = currentBin.GetClipList()
if not clips or not clips[0]:
print("Error: MediaPool bin doesn't contain any clips!")
sys.exit()
def find_boxes(f, start_offset=0, end_offset=float("inf")):
s = struct.Struct("> I 4s")
e = struct.Struct("> Q")
boxes = {}
offset = start_offset
f.seek(offset, 0)
while offset < end_offset:
data = f.read(8) # read box header
if data == b"": break # EOF
length, text = s.unpack(data)
if text == b'uuid':
raise NotImplementedError("Cannot parse UUID box type")
if length == 1:
data = f.read(8)
length = e.unpack(data)[0]
boxes[text] = (offset, offset + length)
offset += length
f.seek(offset) # skip to next box
return boxes
def examine_mp4(filename):
with open(filename, "rb") as f:
boxes = find_boxes(f)
print(f"File {filename}: {boxes}")
# Sanity check that this really is a movie file.
def fileerror(): # function to call if file is not a movie file
print("")
print("ERROR, file is not a mp4-video-file!")
os.system("pause")
exit()
try:
if boxes[b'ftyp'][0] != 0:
fileerror()
except:
fileerror()
moov_boxes = find_boxes(f, boxes[b'moov'][0] + 8, boxes[b'moov'][1])
udta_boxes = find_boxes(f, moov_boxes[b'udta'][0] + 8, moov_boxes[b'udta'][1])
if b'GPMF' in udta_boxes.keys():
### get GPMF Box
highlights = parse_highlights(f, udta_boxes[b'GPMF'][0] + 8, udta_boxes[b'GPMF'][1])
else:
# parsing for versions before Hero6
highlights = parse_highlights_old_version(f, udta_boxes[b'HMMT'][0] + 12, udta_boxes[b'HMMT'][1])
print("")
print("Filename:", filename)
print("Found", len(highlights), "Highlight(s)!")
print('Here are all Highlights: ', highlights)
return highlights
def parse_highlights_old_version(f, start_offset=0, end_offset=float("inf")):
listOfHighlights = []
offset = start_offset
f.seek(offset, 0)
while True:
data = f.read(4)
timestamp = int.from_bytes(data, "big")
if timestamp != 0:
listOfHighlights.append(timestamp)
else:
break
return listOfHighlights
def parse_highlights(f, start_offset=0, end_offset=float("inf")):
inHighlights = False
inHLMT = False
listOfHighlights = []
offset = start_offset
f.seek(offset, 0)
while offset < end_offset:
data = f.read(4)
if data == b"": break
if data == b'High' and inHighlights == False:
data = f.read(4)
if data == b'ligh':
inHighlights = True
if data == b'HLMT' and inHighlights == True and inHLMT == False:
inHLMT = True
if data == b'MANL' and inHighlights == True and inHLMT == True:
currPos = f.tell()
f.seek(currPos - 20)
data = f.read(4)
timestamp = int.from_bytes(data, "big")
if timestamp != 0:
listOfHighlights.append(timestamp)
f.seek(currPos)
return listOfHighlights
for clip in clips:
fmt = clip.GetClipProperty("Format")
file_path = clip.GetClipProperty("File Path")
if fmt != 'QuickTime':
print(f"#{file_path} is not a gopro footage")
continue
# Get clip framerate
fps = clip.GetClipProperty("FPS")
if not fps:
print("Error: Failed to get clip 'Frames' property !")
sys.exit()
highlights = examine_mp4(file_path)
if highlights is not None and len(highlights) > 0 :
for i, highlight in enumerate(highlights):
frameId = floor(highlight * fps / 1000)
isSuccess = clip.AddMarker(frameId, "Yellow", "HiLight", "GoPro HiLight", 1)
@jeromecornet
Copy link
Author

jeromecornet commented Dec 6, 2023

This was tested with Resolve Studio 18.6 on MacOS. You need to install a version of python between 3.6 and 3.11 (3.12 doesn't work with the davinci library).

export RESOLVE_SCRIPT_API="/Library/Application Support/Blackmagic Design/DaVinci Resolve/Developer/Scripting"
export RESOLVE_SCRIPT_LIB="/Applications/DaVinci Resolve/DaVinci Resolve.app/Contents/Libraries/Fusion/fusionscript.so"
export PYTHONPATH="$PYTHONPATH:$RESOLVE_SCRIPT_API/Modules/"
  • Put the script in /Library/Application Support/Blackmagic Design/DaVinci Resolve/Fusion/Scripts/Utility
  • Open Resolve Studio, in Preferences->General, set External scripting using to Local
  • From the media tab, in the bin where your gopro footage is, click on workspace -> scripts -> Mark GoPro HiLight

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment