J'ai fait quelques expériences basées sur la réponse de James Henstridge et j'ai créé un simple script PHP qui a réussi à diviser toutes les photos de mouvement de la Google Camera que je lui ai envoyées.
2022-01-24 - Le code a été mis à jour. Voir la modification au bas de ce message. Je laisse l'ancien code ici pour éviter toute confusion.
<?php
$src_arg = $argv[1];
$src_dir = realpath(pathinfo($src_arg, PATHINFO_DIRNAME));
echo "Scanning for files...\n";
foreach (glob($src_arg) as $src) {
$file = realpath($src);
if (!is_dir($file) && in_array(strtoupper(pathinfo($file, PATHINFO_EXTENSION)), ["JPEG", "JPG"])) {
echo "\tProcessing: " . $file . "\n";
$filesize = filesize($file);
echo "\t\tFile size: " . $filesize . "\n";
$handle = fopen($file, "rb");
$data = fread($handle, $filesize);
fclose($handle);
$eoi_pos = strpos($data, "\xFF\xD9\x00\x00\x00\x18");
echo "\t\tEOI segment position: " . $eoi_pos . "\n";
if ($eoi_pos !== FALSE) {
$output_base = $src_dir . DIRECTORY_SEPARATOR . pathinfo($file, PATHINFO_FILENAME);
echo "\t\tSaving photo...\n";
file_put_contents($output_base . "_photo.jpg", substr($data, 0, $eoi_pos + 2));
echo "\t\tSaving video...\n";
file_put_contents($output_base . "_video.mp4", substr($data, $eoi_pos + 2));
} else {
echo "\t\tSKIPPING - File does not appear to be a Google motion photo.\n";
}
}
}
echo "Done.\n";
?>
Il devrait fonctionner sous Windows et Linux. Il vous suffit de passer un chemin d'accès comme premier argument et il divisera tous les fichiers qu'il croit être des photos de mouvement. Vous pouvez utiliser des caractères génériques. C'est non-destructif - le(s) fichier(s) source(s) ne sont pas supprimés.
Quelques exemples d'utilisation :
php google_motion_photo_splitter.php c:\test\file.jpg
php google_motion_photo_splitter.php c:\test\*.jpg
php google_motion_photo_splitter.php c:\test\*
2022-01-24 Comme j3App a mentionné dans sa réponse A un moment donné, Google a décidé d'ajouter des données de débogage entre le JPG et le MP4 dans certains cas. À la fin de l'année 2021, j'ai également commencé à voir d'autres variations (MP4s avec des octets de départ différents - ex : 0000001C au lieu de 00000018, segments JPG EOI supplémentaires plus tôt dans l'image près des balises XMP, etc). Quoi qu'il en soit, j'ai modifié le code pour qu'il recherche d'abord l'en-tête MP4, puis le segment JPG EOI. Cela a fonctionné sur toutes les variantes que j'ai rencontrées. Voici le code mis à jour :
<?php
$src_arg = $argv[1];
$src_dir = realpath(pathinfo($src_arg, PATHINFO_DIRNAME));
echo "Scanning for files...\n";
foreach (glob($src_arg) as $src) {
$file = realpath($src);
if (!is_dir($file) && in_array(strtoupper(pathinfo($file, PATHINFO_EXTENSION)), ["JPEG", "JPG"])) {
echo "\tProcessing: " . $file . "\n";
$filesize = filesize($file);
echo "\t\tFile size: " . $filesize . "\n";
$handle = fopen($file, "rb");
$data = fread($handle, $filesize);
fclose($handle);
$mp4_start_pos = strpos($data, "ftyp");
if ($mp4_start_pos !== FALSE) {
$mp4_start_pos -= 4; # the real beginning of the mp4 starts 4 bytes before "ftyp"
$jpg_end_pos = strrpos(substr($data, 0, $mp4_start_pos), "\xFF\xD9");
if ($jpg_end_pos !== FALSE) {
$jpg_end_pos += 2; # account for the length of the search string
$output_base = $src_dir . DIRECTORY_SEPARATOR . pathinfo($file, PATHINFO_FILENAME);
echo "\t\tSaving photo...\n";
file_put_contents($output_base . "_photo.jpg", substr($data, 0, $jpg_end_pos));
echo "\t\tSaving video...\n";
file_put_contents($output_base . "_video.mp4", substr($data, $mp4_start_pos));
} else {
echo "\t\tSKIPPING - File appears to contain an MP4 but the no valid JPG EOI segment could be found.\n";
}
} else {
echo "\t\tSKIPPING - File does not appear to be a Google motion photo.\n";
}
}
}
echo "Done.\n";
?>