Wie kann ich die bewegten Fotos von Google Camera auf meinem Windows-PC anzeigen?

Ich kann die JPGs aus meinem DCIM-Ordner in meinem Pixel 2 herunterladen und weiß, dass das Video in der JPG-Datei codiert ist, aber ich weiß nicht, mit welchem ​​Bildbetrachter ich die Bewegung sehen kann.

Normalerweise verwende ich Irfanview, aber ich konnte noch nicht herausfinden, wie ich die Bewegung anzeigen kann. Und ich habe niemanden gefunden, der darüber spricht, wie man das macht.

Weiß jemand?

Antworten (5)

Basierend auf der Antwort von James Henstridge habe ich einige Experimente durchgeführt und ein einfaches PHP-Skript entwickelt, das erfolgreich das bewegte Foto jeder Google-Kamera aufteilt, das ich darauf geworfen habe.

24.01.2022 - Code wurde aktualisiert. Siehe die Bearbeitung am Ende dieses Beitrags. Ich lasse den alten Code hier, um Verwirrung zu vermeiden.

<?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";
  
?>

Es sollte unter Windows und Linux funktionieren. Sie übergeben einfach einen Pfad als erstes Argument und es wird alle Dateien teilen, von denen es glaubt, dass es sich um bewegte Fotos handelt. Sie können Platzhalter verwenden. Es ist nicht destruktiv – die Quelldatei(en) werden nicht gelöscht.

Einige Anwendungsbeispiele:

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 Wie j3App in ihrer Antwort erwähnte , entschied Google irgendwann, in einigen Fällen Debug-Daten zwischen JPG und MP4 hinzuzufügen. Ende 2021 sah ich auch andere Variationen (MP4s mit unterschiedlichen Startbytes – z. B.: 0000001C anstelle von 00000018, zusätzliche JPG-EOI-Segmente früher im Bild in der Nähe von XMP-Tags usw.). Wie auch immer, ich habe den Code geändert, um zuerst nach dem MP4-Header zu suchen und dann rückwärts zu arbeiten, um das JPG-EOI-Segment zu finden. Es hat bei allen Variationen funktioniert, denen ich begegnet bin. Hier ist der aktualisierte Code:

<?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";
  
?>
Danke für eine gute Lösung. Da mein Pixel 4a 5g Debug-Informationen zwischen dem Foto und dem Video hinzufügt, musste ich einige kleine Anpassungen an Ihrer Lösung vornehmen (siehe meine Antwort unten).

Ich habe keine Software gesehen, um sie anders als auf der Google Fotos-Website anzuzeigen. Ich war neugierig, also fing ich an, eines der Fotos von meinem Handy auseinander zu nehmen, und hier ist, was ich fand:

  1. Die Bilddatei scheint ein standardmäßiges JPEG-Bild zu sein, wird aber nach dem Segment „End of Image“ ( 0xFF 0xD9) fortgesetzt.

  2. exiftoolmeldet nicht erkannte MakerNotes. Ich vermute, dass diese benutzerdefinierten Metadaten die Datei als bewegtes Foto identifizieren.

  3. Wenn Sie alle Daten nach dem EOI-Segment in eine separate Datei übertragen, haben Sie einen Standard-MPEG-4-Container. Ich habe einen GStreamer-Absturz bekommen, als ich versuchte, es abzuspielen, aber ffmpeg scheint in der Lage zu sein, damit umzugehen und die folgenden Metadaten anzuzeigen:

    Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'foo.mp4':
      Metadata:
        major_brand     : mp42
        minor_version   : 0
        compatible_brands: isommp42
        creation_time   : 2018-07-07T20:37:57.000000Z
        com.android.version: 8.1.0
      Duration: 00:00:01.87, start: 0.000000, bitrate: 20283 kb/s
        Stream #0:0(eng): Video: h264 (High) (avc1 / 0x31637661), yuvj420p(pc, smpte170m/smpte170m/unknown), 1024x768, 20161 kb/s, SAR 1:1 DAR 4:3, 30.01 fps, 30 tbr, 90k tbn, 180k tbc (default)
        Metadata:
          creation_time   : 2018-07-07T20:37:57.000000Z
          handler_name    : VideoHandle
        Stream #0:1(eng): Data: none (mett / 0x7474656D), 108 kb/s (default)
        Metadata:
          creation_time   : 2018-07-07T20:37:57.000000Z
          handler_name    : MetadHandle
        Stream #0:2(eng): Data: none (mett / 0x7474656D), 0 kb/s (default)
        Metadata:
          creation_time   : 2018-07-07T20:37:57.000000Z
          handler_name    : MetadHandle
    Unsupported codec with id 0 for input stream 1
    Unsupported codec with id 0 for input stream 2
    

    Das ist also ein 1,87 Sekunden langes H.264-Video mit einer Auflösung von 1024 x 768, das ungefähr mit dem übereinzustimmen scheint, was ich auf den Apps/Websites von Google sehe (ein Rückgang der Auflösung und eine Änderung des Seitenverhältnisses).

Ich weiß, dass es keine vollständige Lösung ist, aber es könnte ausreichen, um mit einem Tool zum Extrahieren der Videos zu beginnen.

Obwohl die Frage für Windows gilt, denke ich, dass es angebracht ist, mein Skript hier zu posten, um es von der Befehlszeile unter Linux abzuspielen:

https://gist.github.com/vi/5de17bb8d4ea91b8c28e79e0bac6c3cb

#!/bin/bash

if [[ -z "$1"  || "$1" == --help || "$1" == "-?" ]]; then
    echo "Usage: mvimg_play MVIMG_20190806_183324.jpg [other files]"
    echo "Plays Google's Motion Photo using mpv. Depends on exiftool, mktemp, bash and mpv."
    exit 0
fi

FOUND=0
ARGS=()

TORM=()
TOKILL=()

function cleanup() {
    for i in "${TORM[@]}"; do
        rm -f "$i"
    done
    for p in ${TOKILL[@]}; do
        wait $p
    done
}

trap "cleanup" EXIT

for i in "$@"; do
    O=$(exiftool -t $i  | grep -F 'Micro Video Offset' | cut -f 2-2)
    if [[ -z "$O" ]]; then
        # wrong file? Just appending to playlist as is
        ARGS+=($i)
    else
        FOUND=1
        S=$(find $i -printf '%s')
        T=`mktemp`

        ARGS+=("$T")
        dd if="$i" skip=$((S-O)) iflag=skip_bytes of="$T" 2> /dev/null &
        TOKILL+=($!)
        TORM+=("$T")
    fi
done

if [[ $FOUND == 0 ]]; then
    echo "EXIF tag wasn't detected in specified files. Maybe exiftool does not work?" >&2
fi

mpv "${ARGS[@]}"

Vielleicht kann es auch unter Windows verwendet werden, mit genügend Mingw/Cygwin-Hackery.

Ich habe die Antwort von Kory ausprobiert - danke Kory. Da mein Pixel 4a 5G eine beträchtliche Menge an Debug-Informationen zwischen dem JPG und dem MP4 hinzufügt, wurden meine extrahierten MP4-Dateien nicht abgespielt.

Also musste ich Korys Lösung ein wenig zusätzliche Funktionalität hinzufügen

<?php

/** execute like > php motionPhotoSplitter.php "Camera Roll/*.MP.jpg" < */
const MP4_TYPES = ["avc1", "iso2", "isom", "mmp4", "mp41", "mp42", "mp71", "msnv", "ndas", "ndsc", "ndsh", "ndsm", "ndsp", "ndss", "ndxc", "ndxh", "ndxm", "ndxp", "ndxs"];
$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"); // binary read
        $data = fread($handle, $filesize);
        fclose($handle);

        $eoi_pos = strpos($data, "\xFF\xD9"); // end of image in a jpeg file: #FFD9
        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));

/** an mp4 block starts with 4 bytes for length, then "ftyp", then a valid mp4-type as defined in the array MP4_TYPES */

            $mp4Pos = strpos($data, "ftyp", $eoi_pos);
            if ($mp4Pos && in_array(substr($data, $mp4Pos + 4, 4), MP4_TYPES)) {
                echo "\t\tSaving video...\n";
                file_put_contents($output_base . "_video.mp4", substr($data, $mp4Pos - 4));
            }
        } else {
            echo "\t\tSKIPPING - File does not appear to be a Google motion photo.\n";
        }
    }
}
echo "Done.\n";
?>
Danke, dass du das gepostet hast. Ich habe auch dies sowie einige andere neue Variationen gesehen. Ich habe meinen ursprünglichen Beitrag so aktualisiert, dass er hoffentlich mit allen zukünftigen Änderungen, die Google vornimmt, weiterarbeiten kann.

Es gibt auch Motion-Photo-Viewer . Kann entweder selbst gehostet oder online verwendet werden, um eine Single abzuspielen *.MP.jpg(und die mp4 durch right-click-save-as.

Sie benötigen keinen Webserver, daher index.htmlreicht es aus, das Projekt zu klonen und zu öffnen.