¿Cómo puedo ver las fotos en movimiento de Google Camera en mi PC con Windows?

Puedo descargar los JPG de mi carpeta DCIM en mi Pixel 2 y sé que el video está codificado dentro del JPG, pero no sé qué visor de imágenes puedo usar para ver el movimiento.

Usualmente uso Irfanview, pero aún no he podido descubrir cómo hacer que muestre el movimiento. Y no he encontrado a nadie hablando de cómo hacerlo.

¿Alguien sabe?

Respuestas (5)

Experimenté un poco basándome en la respuesta de James Henstridge y se me ocurrió un script PHP simple que dividió con éxito todas las fotos en movimiento de la cámara de Google que le arrojé.

2022-01-24 - El código ha sido actualizado. Vea la edición al final de esta publicación. Dejo aquí el código antiguo para evitar confusiones.

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

Debería funcionar en Windows y Linux. Simplemente pasa una ruta como primer argumento y dividirá cualquier archivo que crea que son fotos en movimiento. Puede utilizar comodines. No es destructivo: los archivos de origen no se eliminan.

Algunos ejemplos de usos:

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 Como mencionó j3App en su respuesta , en algún momento, Google decidió agregar datos de depuración entre JPG y MP4 en algunos casos. A fines de 2021, también comencé a ver otras variaciones (MP4 con bytes de inicio variables, por ejemplo, 0000001C en lugar de 00000018, segmentos JPG EOI adicionales antes en la imagen cerca de las etiquetas XMP, etc.). De todos modos, modifiqué el código para buscar primero el encabezado MP4 y luego retrocedí para encontrar el segmento JPG EOI. Ha trabajado en todas las variaciones que encontré. Aquí está el código actualizado:

<?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";
  
?>
Gracias por una buena solución. Dado que mi Pixel 4a 5g agrega información de depuración entre la foto y el video, tuve que hacer algunos pequeños ajustes a su solución (vea mi respuesta a continuación)

No he visto ningún software para verlos que no sea en el sitio web de Google Photos. Tenía curiosidad acerca de esto, así que comencé a separar una de las fotos de mi teléfono y esto es lo que encontré:

  1. El archivo de imagen parece ser una imagen JPEG estándar, pero continúa después del segmento Fin de imagen ( 0xFF 0xD9).

  2. exiftoolinforma MakerNotes no reconocidos. Sospecho que estos metadatos personalizados identifican el archivo como una foto en movimiento.

  3. Si transfiere todos los datos después del segmento EOI a un archivo separado, tendrá un contenedor MPEG-4 estándar. Obtuve un bloqueo de GStreamer cuando intentaba reproducirlo, pero parece que ffmpeg puede manejarlo y mostró los siguientes metadatos:

    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
    

    Entonces, ese es un video H.264 de 1.87 segundos con una resolución de 1024x768, que parece coincidir aproximadamente con lo que veo en las aplicaciones/sitio web de Google (una caída en la resolución y un cambio en la relación de aspecto).

Sé que no es una solución completa, pero podría ser suficiente para comenzar con una herramienta para extraer los videos.

Aunque la pregunta es para Windows, creo que es apropiado publicar mi script para reproducirlo desde la línea de comandos en Linux aquí:

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[@]}"

Tal vez también se pueda usar en Windows, con suficiente piratería de Mingw/Cygwin.

Probé la respuesta de Kory , gracias Kory. Dado que mi Pixel 4a 5G agrega una cantidad significativa de información de depuración entre el jpg y el mp4, mis mp4 extraídos no se reprodujeron.

Así que tuve que agregar un poco de funcionalidad adicional a la solución de Kory.

<?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";
?>
Gracias por publicar esto. También he visto esto, así como algunas otras variaciones nuevas. Actualicé mi publicación original para que funcione de una manera que, con suerte, pueda continuar funcionando con cualquier cambio futuro que Google decida realizar.

También hay Motion-Photo-Viewer . Puede ser autohospedado o utilizado en línea para reproducir un solo *.MP.jpg(y extraer el mp4 a través de right-click-save-as.

No necesita un servidor web, por lo que index.htmlbastará con clonar el proyecto y abrirlo.