# Cómo grabar un video con audio y límite de tiempo

> Grabar video (con o sin audio), definir calidad y límite de duración, y obtener la ruta del MP4.

Source: https://crossfly.dev/es/docs/cookbook/device/gravar-video — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# Cómo grabar un video con audio y límite de tiempo

**Propósito.** Grabar un video con la cámara del dispositivo, opcionalmente **con audio**, limitando la
duración y eligiendo la calidad, y recibir la **ruta del archivo `.mp4`**.

## APIs usadas
- `Camera.captureMode` (`Preview`) — modo de captura: `PHOTO`, `MUTED_VIDEO` (video sin sonido), `AUDIO_VIDEO` (video + audio).
- `Camera.videoTimeLimit` (`int`) — límite de grabación en **segundos**; `0` = sin límite.
- `Camera.videoQuality` (`AVQuality`) — `HIGH`, `MEDIUM` (default de `Camera`), `LOW`. Cada valor define fps, tasa de video/audio y estéreo.
- `Camera.shot()` → `String` — inicia la UI de grabación y devuelve la ruta del `.mp4` (o `null`).
- `PhotoPicker(Preview type)` — UI usada cuando `cameraType == PICKER`; tiene los campos públicos `videoTimeLimit` y `videoQuality` que `shot()` ya rellena a partir del `Camera`.

> **Nota.** En el `PhotoPicker`, al grabar video, el archivo se guarda en `/sdcard/<timestamp>.mp4`.
> El `videoTimeLimit` hace que la etiqueta de tiempo del picker **cuente de forma regresiva**. En `AUDIO_VIDEO`
> la grabación incluye audio (`GdxRecorder.startVideoWithAudio`), en `MUTED_VIDEO` no.
> En iOS con modo de video, `shot()` usa la cámara nativa del SO pasando `videoTimeLimit`/`videoQuality`.

## Ejemplo completo (copiar, pegar y ejecutar)

```java
package com.exemplo;

import dev.crossfly.ui.*;                   // Window, Component, Button, Label, UIConstants
import dev.crossfly.ui.layout.*;            // VerticalLayout
import dev.crossfly.device.*;               // Camera, Camera.AVQuality

/** Pantalla principal ejecutable. Apunta el launcher (classname) y corre: gradlew :lwjgl3:run */
public class MainWindow extends Window
{
   @Override
   public void onShow() throws Exception
   {
      super.onShow();
      setLayout(new VerticalLayout());

      Label resultado = new Label("");
      add(LAY_AUTO, resultado);

      Button acao = new Button("Gravar vídeo");
      acao.onAction(c ->
      {
         new Thread(() ->
         {
            Camera cam = new Camera();
            cam.cameraType     = UIConstants.CameraType.PICKER;     // UI de grabación de CrossFly
            cam.captureMode    = UIConstants.Preview.AUDIO_VIDEO;   // vídeo CON audio (usa MUTED_VIDEO p/ sin sonido)
            cam.videoQuality   = Camera.AVQuality.HIGH;             // HIGH | MEDIUM | LOW
            cam.videoTimeLimit = 30;                                // 30s; 0 = sin límite

            String mp4 = cam.shot();                                // ruta del .mp4, o null si se canceló

            Component.onUI(() ->                                    // retorno fuera de la paint thread
               resultado.setText(mp4 != null ? "Vídeo: " + mp4 : "Cancelado"));
         }).start();
      });
      add(LAY_AUTO, acao);
   }
}
```

> El fragmento de abajo muestra **solo el núcleo**. Para ejecutarlo, colócalo en el esqueleto `MainWindow` de arriba.

## Fragmento mínimo

```java
// verificado en la fuente: Camera.java (captureMode/videoTimeLimit/videoQuality, shot()); PhotoPicker.java (startRecording, stopRecording)
import dev.crossfly.device.*;              // Camera, Camera.AVQuality
import dev.crossfly.ui.*;                  // Component, UIConstants.CameraType/Preview

new Thread(() -> {
   Camera cam = new Camera();
   cam.cameraType    = UIConstants.CameraType.PICKER;     // UI de grabación de CrossFly
   cam.captureMode   = UIConstants.Preview.AUDIO_VIDEO;   // vídeo CON audio (usa MUTED_VIDEO p/ sin sonido)
   cam.videoQuality  = Camera.AVQuality.HIGH;             // HIGH | MEDIUM | LOW
   cam.videoTimeLimit = 30;                               // 30s; 0 = sin límite

   String mp4 = cam.shot();                               // ruta del .mp4, o null si se canceló

   Component.onUI(() -> {                                 // retorno fuera de la paint thread
      if (mp4 != null)
         usarVideo(mp4);
   });
}).start();
```

## Profundiza
- Guía de la cámara: [Cámara del dispositivo](/docs/data-and-services/device-camera) · Widget: [PhotoPicker](/docs/ui/widgets/photopicker)
- Receta hermana: [tirar-foto.md](/docs/cookbook/device/tirar-foto)
