Para abrir un video cuando usas ./own-obs.sh 0 0 614 768 :

url="$1"
id="${url##*embed/}"

yt-dlp $1 &&

mplayer -vo fbdev2 -vf scale=1366:768,crop=608:768:379:0 -geometry 0%:0% *$id*

Graba la pantalla, audio y cámara desde Linux TTY, increíble.

Esto uso para grabar TTY y EGLFS.


#!/bin/bash
# Uso: ./shorts.sh [x y w h]
# Sin argumentos: usa región central 4:5 automática
# Ejemplo: ./shorts.sh 200 0 500 625

v4l2-ctl --device=/dev/video0 --set-fmt-video=width=1280,height=720,pixelformat=YUYV


TIMESTAMP=$(date +%Y%m%d_%H%M%S)
AUDIO_FILE="$HOME/audio_${TIMESTAMP}.wav"
VIDEO_FILE="$HOME/recording_${TIMESTAMP}.mp4"
CAM_FILE="$HOME/cam_${TIMESTAMP}.mp4"
OUTPUT_FILE="$HOME/final_${TIMESTAMP}.mp4"

SCREEN_W=1366
SCREEN_H=768

# ── Resolución de salida (cambiá a 480 si sigue trabándose) ──
# 720p → ancho 720, alto 900 (formato 4:5)
# 480p → ancho 480, alto 600
OUT_W=720
OUT_H=900
CAM_OUT_W=720
# CAM_OUT_H se calcula en el filter_complex con -2 para que siempre sea par

# ── Región a capturar ────────────────────────────────────────
if [ $# -eq 4 ]; then
    CROP_X=$1; CROP_Y=$2; CROP_W=$3; CROP_H=$4
    echo "Región manual: ${CROP_W}x${CROP_H} en offset ${CROP_X},${CROP_Y}"
else
    # Toda la altura (768), ancho 4:5 = 614, centrado
    CROP_H=$SCREEN_H
    CROP_W=$(( CROP_H * 4 / 5 & ~1 ))
    CROP_X=$(( (SCREEN_W - CROP_W) / 2 ))
    CROP_Y=0
    echo "Región central 4:5 automática: ${CROP_W}x${CROP_H} offset ${CROP_X},${CROP_Y}"
    echo "Podés especificar región: ./shorts.sh x y ancho alto"
fi
CROP_W=$(( CROP_W & ~1 ))
CROP_H=$(( CROP_H & ~1 ))

MPLAYER_PID=""; AUDIO_PID=""; CAM_PID=""

# ── Subida a YouTube ─────────────────────────────────────────
upload_youtube() {
    local VIDEO_PATH="$1"

    echo ""
    read -r -p "¿Subir a YouTube? [s/N]: " RESPUESTA
    if [[ ! "$RESPUESTA" =~ ^[sS]$ ]]; then
        echo "Listo. Video guardado en: $VIDEO_PATH"
        return
    fi

    read -r -p "Título: " YT_TITLE
    read -r -p "Descripción: " YT_DESC

    echo "Privacidad:"
    echo "  1) Público"
    echo "  2) No listado"
    echo "  3) Privado"
    read -r -p "Elegí [1-3] (default: 2): " YT_PRIV_OPT
    case "$YT_PRIV_OPT" in
        1) YT_PRIVACY="public"   ;;
        3) YT_PRIVACY="private"  ;;
        *) YT_PRIVACY="unlisted" ;;
    esac

    echo ""
    echo "Subiendo '$YT_TITLE' como [$YT_PRIVACY]..."
    source "$HOME/youtube-upload/bin/activate"
    "$HOME/youtube-upload/youtube-upload/bin/youtube-upload" \
        --title="$YT_TITLE" \
        --description="$YT_DESC" \
        --recording-date="$(date +%Y-%m-%dT%H:%M:%S.0Z)" \
        --privacy="$YT_PRIVACY" \
        --embeddable=True \
        "$VIDEO_PATH"

    if [ $? -eq 0 ]; then
        echo "✓ Video subido exitosamente."
    else
        echo "✗ Error al subir. El video sigue en: $VIDEO_PATH"
    fi
}

cleanup() {
    [ -n "$MPLAYER_PID" ] && kill "$MPLAYER_PID" 2>/dev/null
    [ -n "$AUDIO_PID" ]   && kill "$AUDIO_PID"   2>/dev/null
    [ -n "$CAM_PID" ]     && kill "$CAM_PID"      2>/dev/null
    wait 2>/dev/null

    if [ -f "$VIDEO_FILE" ] && [ -f "$CAM_FILE" ] && [ -f "$AUDIO_FILE" ]; then
        echo "Combinando video, cámara y audio..."
        ffmpeg \
            -i "$VIDEO_FILE" \
            -i "$CAM_FILE" \
            -i "$AUDIO_FILE" \
            -filter_complex "
                [0:v]scale=${OUT_W}:${OUT_H},setsar=1[screen];
                [1:v]scale=${CAM_OUT_W}:-2,setsar=1[cam];
                [screen][cam]vstack=inputs=2[stacked];
                [stacked]drawbox=x=0:y=0:w=iw:h=ih:color=red@0.9:t=18[framed];
                [framed]drawbox=x=8:y=8:w=iw-16:h=ih-16:color=red@0.5:t=4,
                drawtext=text='● REC':fontcolor=red:fontsize=40:x=20:y=20:
                    fontfile=/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf[out]
            " \
            -map "[out]" -map "2:a" \
            -c:v libx264 -preset ultrafast -crf 30 \
            -c:a aac -b:a 96k -shortest \
            "$OUTPUT_FILE" -y
        rm -f "$AUDIO_FILE" "$VIDEO_FILE" "$CAM_FILE"
        echo "Guardado en: $OUTPUT_FILE"

        upload_youtube "$OUTPUT_FILE"
    fi
    exit 0
}

trap 'cleanup' EXIT INT TERM

# ── Preview cámara ───────────────────────────────────────────
mplayer -vo fbdev2 \
    -tv driver=v4l2:device=/dev/video0:width=640:height=480 tv:// \
    -zoom -geometry 90%:90% &
MPLAYER_PID=$!

# ── Grabar cámara (640x480 → mejor calidad que 320x240) ──────
ffmpeg -f v4l2 -video_size 640x480 -framerate 30 -i /dev/video0 \
    -c:v libx264 -preset ultrafast -crf 28 \
    "$CAM_FILE" -y &
CAM_PID=$!

# ── Grabar audio ─────────────────────────────────────────────
MONITOR=$(pactl list sources short | grep monitor | head -1 | awk '{print $2}')
ffmpeg -f pulse -i "$MONITOR" -f pulse -i default \
    -filter_complex "[0:a][1:a]amix=inputs=2[aout]" \
    -map "[aout]" "$AUDIO_FILE" -y &
AUDIO_PID=$!

# ── Grabar región del framebuffer sin estirar ────────────────
# kmsgrab captura pantalla completa → bajamos a CPU → crop → scale → subimos a VAAPI
sudo LIBVA_DRIVER_NAME=i965 ffmpeg \
    -f kmsgrab -framerate 30 -device /dev/dri/card1 -i - \
    -vaapi_device /dev/dri/renderD128 \
    -vf "hwmap=derive_device=vaapi,\
scale_vaapi=w=${SCREEN_W}:h=${SCREEN_H}:format=nv12,\
hwdownload,format=nv12,\
crop=${CROP_W}:${CROP_H}:${CROP_X}:${CROP_Y},\
scale=${OUT_W}:${OUT_H},\
hwupload,scale_vaapi=format=nv12" \
    -c:v h264_vaapi -global_quality 28 \
    "$VIDEO_FILE" -y

#!/bin/bash

TIMESTAMP=$(date +%Y%m%d_%H%M%S)
AUDIO_FILE="$HOME/audio_${TIMESTAMP}.wav"
VIDEO_FILE="$HOME/recording_${TIMESTAMP}.mp4"
OUTPUT_FILE="$HOME/final_${TIMESTAMP}.mp4"

MPLAYER_PID=""
ARECORD_PID=""

cleanup() {
    [ -n "$MPLAYER_PID" ] && kill "$MPLAYER_PID" 2>/dev/null
    [ -n "$ARECORD_PID" ] && kill "$ARECORD_PID" 2>/dev/null
    wait 2>/dev/null
    if [ -f "$AUDIO_FILE" ] && [ -f "$VIDEO_FILE" ]; then
        ffmpeg -i "$VIDEO_FILE" -i "$AUDIO_FILE" -c:v copy -c:a aac -shortest "$OUTPUT_FILE" -y
        rm -rf "$AUDIO_FILE" "$VIDEO_FILE"
    fi
    exit 0
}

trap 'cleanup' EXIT INT TERM

mplayer -vo fbdev2 -tv driver=v4l2:device=/dev/video0:width=120:height=140 tv:// -vf "scale=320:240" -zoom -geometry 90%:90% &
MPLAYER_PID=$!

arecord -f cd -t wav "$AUDIO_FILE" &
ARECORD_PID=$!

sudo LIBVA_DRIVER_NAME=i965 ffmpeg -f kmsgrab -framerate 30 -device /dev/dri/card1 -i - -vaapi_device /dev/dri/renderD128 -vf 'hwmap=derive_device=vaapi,scale_vaapi=w=1366:h=768:format=nv12' -c:v h264_vaapi -global_quality 25 "$VIDEO_FILE" -y

Grabar navegador con la camara abajo.

#!/bin/bash

TIMESTAMP=$(date +%Y%m%d_%H%M%S)
AUDIO_FILE="$HOME/audio_${TIMESTAMP}.wav"
VIDEO_FILE="$HOME/recording_${TIMESTAMP}.mp4"
CAM_FILE="$HOME/cam_${TIMESTAMP}.mp4"
OUTPUT_FILE="$HOME/final_${TIMESTAMP}.mp4"

AUDIO_PID=""
CAM_PID=""

# Tamaño del pip (cámara superpuesta)
PIP_W=320
PIP_H=240
# Resolución de pantalla grabada
SCR_W=1366
SCR_H=768
# Posición esquina inferior derecha con margen de 20px
PIP_X=$((SCR_W - PIP_W - 20))
PIP_Y=$((SCR_H - PIP_H - 20))

cleanup() {
    [ -n "$AUDIO_PID" ]   && kill "$AUDIO_PID"   2>/dev/null
    [ -n "$CAM_PID" ]     && kill "$CAM_PID"      2>/dev/null
    wait 2>/dev/null

    if [ -f "$VIDEO_FILE" ] && [ -f "$CAM_FILE" ] && [ -f "$AUDIO_FILE" ]; then
        echo "Combinando video, cámara y audio..."

        ffmpeg \
            -i "$VIDEO_FILE" \
            -i "$CAM_FILE" \
            -i "$AUDIO_FILE" \
            -filter_complex "
                [0:v]scale=${SCR_W}:${SCR_H},setsar=1[screen];
                [1:v]scale=${PIP_W}:${PIP_H},setsar=1[cam];
                [screen][cam]overlay=x=${PIP_X}:y=${PIP_Y}[pip];
                [pip]drawtext=text='● REC':fontcolor=red:fontsize=36:x=20:y=20:
                    fontfile=/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf[out]
            " \
            -map "[out]" \
            -map "2:a" \
            -c:v libx264 -preset fast -crf 23 \
            -c:a aac \
            -shortest \
            "$OUTPUT_FILE" -y

        rm -f "$AUDIO_FILE" "$VIDEO_FILE" "$CAM_FILE"
        echo "Guardado en: $OUTPUT_FILE"
    fi
    exit 0
}

trap 'cleanup' EXIT INT TERM

# Grabar cámara
ffmpeg -f v4l2 -video_size 320x240 -framerate 30 -i /dev/video0 \
    -c:v libx264 -preset ultrafast -crf 28 \
    "$CAM_FILE" -y &
CAM_PID=$!

# Grabar audio interno + micrófono mezclados
MONITOR=$(pactl list sources short | grep monitor | head -1 | awk '{print $2}')
ffmpeg -f pulse -i "$MONITOR" -f pulse -i default \
    -filter_complex "[0:a][1:a]amix=inputs=2[aout]" \
    -map "[aout]" \
    "$AUDIO_FILE" -y &
AUDIO_PID=$!

# Grabar pantalla
sudo LIBVA_DRIVER_NAME=i965 ffmpeg \
    -f kmsgrab -framerate 30 -device /dev/dri/card1 -i - \
    -vaapi_device /dev/dri/renderD128 \
    -vf 'hwmap=derive_device=vaapi,scale_vaapi=w=1366:h=768:format=nv12' \
    -c:v h264_vaapi -global_quality 25 \
    "$VIDEO_FILE" -y

Short maker

Empezá a escribir desde la 4ta linea

bash shortmaker.sh 0 0 614 768

#!/bin/bash
# Uso: ./shorts.sh [x y w h]
# Sin argumentos: usa región central 4:5 automática
# Ejemplo: ./shorts.sh 200 0 500 625

TIMESTAMP=$(date +%Y%m%d_%H%M%S)
AUDIO_FILE="$HOME/audio_${TIMESTAMP}.wav"
VIDEO_FILE="$HOME/recording_${TIMESTAMP}.mp4"
CAM_FILE="$HOME/cam_${TIMESTAMP}.mp4"
OUTPUT_FILE="$HOME/final_${TIMESTAMP}.mp4"

SCREEN_W=1366
SCREEN_H=768

# ── Región a capturar ────────────────────────────────────────
if [ $# -eq 4 ]; then
    CROP_X=$1; CROP_Y=$2; CROP_W=$3; CROP_H=$4
    echo "Región manual: ${CROP_W}x${CROP_H} en offset ${CROP_X},${CROP_Y}"
else
    # Toda la altura (768), ancho 4:5 = 614, centrado
    CROP_H=$SCREEN_H
    CROP_W=$(( CROP_H * 4 / 5 & ~1 ))
    CROP_X=$(( (SCREEN_W - CROP_W) / 2 ))
    CROP_Y=0
    echo "Región central 4:5 automática: ${CROP_W}x${CROP_H} offset ${CROP_X},${CROP_Y}"
    echo "Podés especificar región: ./shorts.sh x y ancho alto"
fi
CROP_W=$(( CROP_W & ~1 ))
CROP_H=$(( CROP_H & ~1 ))

MPLAYER_PID=""; AUDIO_PID=""; CAM_PID=""

# ── Subida a YouTube ─────────────────────────────────────────
upload_youtube() {
    local VIDEO_PATH="$1"

    echo ""
    read -r -p "¿Subir a YouTube? [s/N]: " RESPUESTA
    if [[ ! "$RESPUESTA" =~ ^[sS]$ ]]; then
        echo "Listo. Video guardado en: $VIDEO_PATH"
        return
    fi

    read -r -p "Título: " YT_TITLE
    read -r -p "Descripción: " YT_DESC

    echo "Privacidad:"
    echo "  1) Público"
    echo "  2) No listado"
    echo "  3) Privado"
    read -r -p "Elegí [1-3] (default: 2): " YT_PRIV_OPT
    case "$YT_PRIV_OPT" in
        1) YT_PRIVACY="public"   ;;
        3) YT_PRIVACY="private"  ;;
        *) YT_PRIVACY="unlisted" ;;
    esac

    echo ""
    echo "Subiendo '$YT_TITLE' como [$YT_PRIVACY]..."
    source "$HOME/youtube-upload/bin/activate"
    "$HOME/youtube-upload/youtube-upload/bin/youtube-upload" \
        --title="$YT_TITLE" \
        --description="$YT_DESC" \
        --recording-date="$(date +%Y-%m-%dT%H:%M:%S.0Z)" \
        --privacy="$YT_PRIVACY" \
        --embeddable=True \
        "$VIDEO_PATH"

    if [ $? -eq 0 ]; then
        echo "✓ Video subido exitosamente."
    else
        echo "✗ Error al subir. El video sigue en: $VIDEO_PATH"
    fi
}

cleanup() {
    [ -n "$MPLAYER_PID" ] && kill "$MPLAYER_PID" 2>/dev/null
    [ -n "$AUDIO_PID" ]   && kill "$AUDIO_PID"   2>/dev/null
    [ -n "$CAM_PID" ]     && kill "$CAM_PID"      2>/dev/null
    wait 2>/dev/null

    if [ -f "$VIDEO_FILE" ] && [ -f "$CAM_FILE" ] && [ -f "$AUDIO_FILE" ]; then
        echo "Combinando video, cámara y audio..."
        ffmpeg \
            -i "$VIDEO_FILE" \
            -i "$CAM_FILE" \
            -i "$AUDIO_FILE" \
            -filter_complex "
                [0:v]scale=1080:1350,setsar=1[screen];
                [1:v]scale=1080:570,setsar=1[cam];
                [screen][cam]vstack=inputs=2[stacked];
                [stacked]drawbox=x=0:y=0:w=iw:h=ih:color=red@0.9:t=18[framed];
                [framed]drawbox=x=8:y=8:w=iw-16:h=ih-16:color=red@0.5:t=4,
                drawtext=text='● REC':fontcolor=red:fontsize=60:x=30:y=30:
                    fontfile=/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf[out]
            " \
            -map "[out]" -map "2:a" \
            -c:v libx264 -preset ultrafast -crf 28 \
            -c:a aac -b:a 128k -shortest \
            "$OUTPUT_FILE" -y
        rm -f "$AUDIO_FILE" "$VIDEO_FILE" "$CAM_FILE"
        echo "Guardado en: $OUTPUT_FILE"

        upload_youtube "$OUTPUT_FILE"
    fi
    exit 0
}

trap 'cleanup' EXIT INT TERM

# ── Preview cámara ───────────────────────────────────────────
mplayer -vo fbdev2 \
    -tv driver=v4l2:device=/dev/video0:width=320:height=240 tv:// \
    -zoom -geometry 90%:90% &
MPLAYER_PID=$!

# ── Grabar cámara ────────────────────────────────────────────
ffmpeg -f v4l2 -video_size 320x240 -framerate 30 -i /dev/video0 \
    -c:v libx264 -preset ultrafast -crf 28 \
    "$CAM_FILE" -y &
CAM_PID=$!

# ── Grabar audio ─────────────────────────────────────────────
MONITOR=$(pactl list sources short | grep monitor | head -1 | awk '{print $2}')
ffmpeg -f pulse -i "$MONITOR" -f pulse -i default \
    -filter_complex "[0:a][1:a]amix=inputs=2[aout]" \
    -map "[aout]" "$AUDIO_FILE" -y &
AUDIO_PID=$!

# ── Grabar región del framebuffer sin estirar ────────────────
# kmsgrab captura pantalla completa → bajamos a CPU → crop → scale → subimos a VAAPI
sudo LIBVA_DRIVER_NAME=i965 ffmpeg \
    -f kmsgrab -framerate 30 -device /dev/dri/card1 -i - \
    -vaapi_device /dev/dri/renderD128 \
    -vf "hwmap=derive_device=vaapi,\
scale_vaapi=w=${SCREEN_W}:h=${SCREEN_H}:format=nv12,\
hwdownload,format=nv12,\
crop=${CROP_W}:${CROP_H}:${CROP_X}:${CROP_Y},\
scale=1080:1350,\
hwupload,scale_vaapi=format=nv12" \
    -c:v h264_vaapi -global_quality 25 \
    "$VIDEO_FILE" -y

.vimrc (columna color y la primer linea no aparece

" Vim Configuration based on blog posts

" ============================================ " BASIC SETTINGS " ============================================

set number

" Marca visual del límite de la región grabada " Fuente latarcyrheb-sun32 = 16px por carácter, región 614px → 38 cols, -1 offset set colorcolumn=34 highlight ColorColumn ctermbg=red ctermfg=white

" Resaltar texto que se pase del límite highlight OverLength ctermbg=darkred ctermfg=white match OverLength /%38v.+/

set nocompatible syntax on “set number “set relativenumber set background=dark set hlsearch set incsearch set ignorecase set noswapfile set ai set ic

" Tab settings set tabstop=4 set shiftwidth=4 set expandtab

" Mouse support set mouse=

" Color scheme colorscheme default

" ============================================ " COPY & PASTE " ============================================ " Copy to clipboard in Wayland xnoremap <C-@> :w !wl-copy

" ============================================ " SPELL CHECK " ============================================ " Download spell files: " mkdir -p ~/.vim/spell " cd ~/.vim/spell " wget –no-check-certificate https://ftp.nluug.nl/vim/runtime/spell/es.latin1.spl " wget –no-check-certificate https://ftp.nluug.nl/vim/runtime/spell/es.latin1.sug " wget –no-check-certificate https://ftp.nluug.nl/vim/runtime/spell/es.utf-8.spl " wget –no-check-certificate https://ftp.nluug.nl/vim/runtime/spell/es.utf-8.sug setlocal spell spelllang=en,es

" ============================================ " HEXADECIMAL EDITING " ============================================ augroup Binary au! au BufReadPre *.bin let &bin=1 au BufReadPost *.bin if &bin | %!xxd | endif au BufReadPost *.bin set ft=xxd au BufWritePre *.bin if &bin | %!xxd -r | endif au BufWritePost *.bin if &bin | %!xxd | endif au BufWritePost *.bin set nomod augroup END

" ============================================ " TMUX/SCREEN KEYBINDINGS " ============================================ if &term =~ ‘^screen’ execute “set =\e[1;*A” execute “set =\e[1;*B” execute “set =\e[1;*C” execute “set =\e[1;*D” endif

" ============================================ " VIM-PLUG SETUP (optional) " ============================================ " Install: curl -fLo ~/.vim/autoload/plug.vim –create-dirs
" https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim " Then run :PlugInstall

" call plug#begin(’~/.vim/plugged’) " Plug ‘junegunn/fzf’, { ‘do’: { -> fzf#install() } } " Plug ‘junegunn/fzf.vim’ " Plug ‘gruvbox-community/gruvbox’ " Plug ’nanotech/jellybeans.vim’ " call plug#end()

Mejor calidad camara


#!/bin/bash
# Uso: ./shorts.sh [x y w h]
# Sin argumentos: usa región central 4:5 automática
# Ejemplo: ./shorts.sh 200 0 500 625

# ── Configuración de cámara ──────────────────────────────────
# Resolución y formato (640x480 en lugar de 320x240)
v4l2-ctl --device=/dev/video0 --set-fmt-video=width=640,height=480,pixelformat=YUYV

# Ajustes de imagen (descomenta y ajustá según tu cámara)
# Listá los controles disponibles con: v4l2-ctl -d /dev/video0 --list-ctrls
v4l2-ctl --device=/dev/video0 \
    --set-ctrl=brightness=10 \
    --set-ctrl=contrast=128 \
    --set-ctrl=saturation=150 \
    --set-ctrl=sharpness=128 \
    --set-ctrl=white_balance_automatic=1 \
    --set-ctrl=auto_exposure=3 2>/dev/null || true
    # auto_exposure: 1=manual, 3=auto (varía por cámara)
    # Si tu cámara soporta backlight_compensation:
    # --set-ctrl=backlight_compensation=1

TIMESTAMP=$(date +%Y%m%d_%H%M%S)
AUDIO_FILE="$HOME/audio_${TIMESTAMP}.wav"
VIDEO_FILE="$HOME/recording_${TIMESTAMP}.mp4"
CAM_FILE="$HOME/cam_${TIMESTAMP}.mp4"
OUTPUT_FILE="$HOME/final_${TIMESTAMP}.mp4"

SCREEN_W=1366
SCREEN_H=768

# ── Región a capturar ────────────────────────────────────────
if [ $# -eq 4 ]; then
    CROP_X=$1; CROP_Y=$2; CROP_W=$3; CROP_H=$4
    echo "Región manual: ${CROP_W}x${CROP_H} en offset ${CROP_X},${CROP_Y}"
else
    CROP_H=$SCREEN_H
    CROP_W=$(( CROP_H * 4 / 5 & ~1 ))
    CROP_X=$(( (SCREEN_W - CROP_W) / 2 ))
    CROP_Y=0
    echo "Región central 4:5 automática: ${CROP_W}x${CROP_H} offset ${CROP_X},${CROP_Y}"
    echo "Podés especificar región: ./shorts.sh x y ancho alto"
fi
CROP_W=$(( CROP_W & ~1 ))
CROP_H=$(( CROP_H & ~1 ))

MPLAYER_PID=""; AUDIO_PID=""; CAM_PID=""

# ── Subida a YouTube ─────────────────────────────────────────
upload_youtube() {
    local VIDEO_PATH="$1"

    echo ""
    read -r -p "¿Subir a YouTube? [s/N]: " RESPUESTA
    if [[ ! "$RESPUESTA" =~ ^[sS]$ ]]; then
        echo "Listo. Video guardado en: $VIDEO_PATH"
        return
    fi

    read -r -p "Título: " YT_TITLE
    read -r -p "Descripción: " YT_DESC

    echo "Privacidad:"
    echo "  1) Público"
    echo "  2) No listado"
    echo "  3) Privado"
    read -r -p "Elegí [1-3] (default: 2): " YT_PRIV_OPT
    case "$YT_PRIV_OPT" in
        1) YT_PRIVACY="public"   ;;
        3) YT_PRIVACY="private"  ;;
        *) YT_PRIVACY="unlisted" ;;
    esac

    echo ""
    echo "Subiendo '$YT_TITLE' como [$YT_PRIVACY]..."
    source "$HOME/youtube-upload/bin/activate"
    "$HOME/youtube-upload/youtube-upload/bin/youtube-upload" \
        --title="$YT_TITLE" \
        --description="$YT_DESC" \
        --recording-date="$(date +%Y-%m-%dT%H:%M:%S.0Z)" \
        --privacy="$YT_PRIVACY" \
        --embeddable=True \
        "$VIDEO_PATH"

    if [ $? -eq 0 ]; then
        echo "✓ Video subido exitosamente."
    else
        echo "✗ Error al subir. El video sigue en: $VIDEO_PATH"
    fi
}

cleanup() {
    [ -n "$MPLAYER_PID" ] && kill "$MPLAYER_PID" 2>/dev/null
    [ -n "$AUDIO_PID" ]   && kill "$AUDIO_PID"   2>/dev/null
    [ -n "$CAM_PID" ]     && kill "$CAM_PID"      2>/dev/null
    wait 2>/dev/null

    if [ -f "$VIDEO_FILE" ] && [ -f "$CAM_FILE" ] && [ -f "$AUDIO_FILE" ]; then
        echo "Combinando video, cámara y audio..."
        ffmpeg \
            -i "$VIDEO_FILE" \
            -i "$CAM_FILE" \
            -i "$AUDIO_FILE" \
            -filter_complex "
                [0:v]scale=1080:1350,setsar=1[screen];
                [1:v]scale=1080:570,
                    unsharp=3:3:0.8:3:3:0,
                    eq=brightness=0.02:contrast=1.05:saturation=1.1,
                    setsar=1[cam];
                [screen][cam]vstack=inputs=2[stacked];
                [stacked]drawbox=x=0:y=0:w=iw:h=ih:color=red@0.9:t=18[framed];
                [framed]drawbox=x=8:y=8:w=iw-16:h=ih-16:color=red@0.5:t=4,
                drawtext=text='● REC':fontcolor=red:fontsize=60:x=30:y=30:
                    fontfile=/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf[out]
            " \
            -map "[out]" -map "2:a" \
            -c:v libx264 -preset ultrafast -crf 28 \
            -c:a aac -b:a 128k -shortest \
            "$OUTPUT_FILE" -y
        rm -f "$AUDIO_FILE" "$VIDEO_FILE" "$CAM_FILE"
        echo "Guardado en: $OUTPUT_FILE"

        upload_youtube "$OUTPUT_FILE"
    fi
    exit 0
}

trap 'cleanup' EXIT INT TERM

# ── Preview cámara ───────────────────────────────────────────
# Resolución de preview aumentada a 640x480
mplayer -vo fbdev2 \
    -tv driver=v4l2:device=/dev/video0:width=640:height=480 tv:// \
    -zoom -geometry 90%:90% &
MPLAYER_PID=$!

# ── Grabar cámara ────────────────────────────────────────────
# Resolución 640x480, sin estiramiento al grabar
# Se escala a 1080x570 solo en el merge final con filtros de mejora
ffmpeg \
    -f v4l2 \
    -video_size 640x480 \
    -framerate 30 \
    -i /dev/video0 \
    -vf "unsharp=3:3:0.5:3:3:0,eq=brightness=0.02:contrast=1.03:saturation=1.08" \
    -c:v libx264 -preset ultrafast -crf 23 \
    "$CAM_FILE" -y &
CAM_PID=$!

# ── Grabar audio ─────────────────────────────────────────────
MONITOR=$(pactl list sources short | grep monitor | head -1 | awk '{print $2}')
ffmpeg -f pulse -i "$MONITOR" -f pulse -i default \
    -filter_complex "[0:a][1:a]amix=inputs=2[aout]" \
    -map "[aout]" "$AUDIO_FILE" -y &
AUDIO_PID=$!

# ── Grabar región del framebuffer sin estirar ────────────────
sudo LIBVA_DRIVER_NAME=i965 ffmpeg \
    -f kmsgrab -framerate 30 -device /dev/dri/card1 -i - \
    -vaapi_device /dev/dri/renderD128 \
    -vf "hwmap=derive_device=vaapi,\
scale_vaapi=w=${SCREEN_W}:h=${SCREEN_H}:format=nv12,\
hwdownload,format=nv12,\
crop=${CROP_W}:${CROP_H}:${CROP_X}:${CROP_Y},\
scale=1080:1350,\
hwupload,scale_vaapi=format=nv12" \
    -c:v h264_vaapi -global_quality 25 \
    "$VIDEO_FILE" -y

Con mplayer y -vo fbdev2 el equivalente es:

mplayer -vo fbdev2 -vf scale=614:752 -geometry 376:16 video.mp4
  • -vf scale=614:752 → escala el video a ese tamaño exacto
  • -geometry 376:16 → lo posiciona en X:Y en el framebuffer

Si el video es 16:9 y no querés que se estire, dejás que calcule la altura proporcional:

mplayer -vo fbdev2 -vf scale=614:-2 -geometry 376:16 video.mp4

El -2 le dice “calculá la altura manteniendo proporción y que sea par”. Para un video 16:9 quedaría en 346px de alto, centrado verticalmente en el frame grabado.

Si querés que ocupe toda la altura disponible (752px) y se recorte lo que sobre horizontalmente, usás zoom:

mplayer -vo fbdev2 -vf scale=-2:752 -zoom -geometry 376:16 video.mp4

Resumen rápido:

Caso Comando
Ocupa exacto el frame (puede estirar) -vf scale=614:752 -geometry 376:16
Proporcional, ancho fijo -vf scale=614:-2 -geometry 376:16
Proporcional, alto fijo -vf scale=-2:752 -geometry 376:16
#!/bin/bash
# Uso: ./shorts.sh [x y w h]
# Sin argumentos: usa región central 4:5 automática
# Ejemplo: ./shorts.sh 200 0 500 625

# ── Configuración de cámara ──────────────────────────────────
v4l2-ctl --device=/dev/video0 --set-fmt-video=width=640,height=480,pixelformat=YUYV

v4l2-ctl --device=/dev/video0 \
    --set-ctrl=brightness=10 \
    --set-ctrl=contrast=128 \
    --set-ctrl=saturation=150 \
    --set-ctrl=sharpness=128 \
    --set-ctrl=white_balance_automatic=1 \
    --set-ctrl=auto_exposure=3 2>/dev/null || true

TIMESTAMP=$(date +%Y%m%d_%H%M%S)
AUDIO_FILE="$HOME/audio_${TIMESTAMP}.wav"
VIDEO_FILE="$HOME/recording_${TIMESTAMP}.mp4"
CAM_FILE="$HOME/cam_${TIMESTAMP}.mp4"
OUTPUT_FILE="$HOME/final_${TIMESTAMP}.mp4"

SCREEN_W=1366
SCREEN_H=768

# ── Región a capturar ────────────────────────────────────────
if [ $# -eq 4 ]; then
    CROP_X=$1; CROP_Y=$2; CROP_W=$3; CROP_H=$4
    echo "Región manual: ${CROP_W}x${CROP_H} en offset ${CROP_X},${CROP_Y}"
else
    CROP_H=$SCREEN_H
    CROP_W=$(( CROP_H * 4 / 5 & ~1 ))
    CROP_X=$(( (SCREEN_W - CROP_W) / 2 ))
    CROP_Y=0
    echo "Región central 4:5 automática: ${CROP_W}x${CROP_H} offset ${CROP_X},${CROP_Y}"
    echo "Podés especificar región: ./shorts.sh x y ancho alto"
fi
CROP_W=$(( CROP_W & ~1 ))
CROP_H=$(( CROP_H & ~1 ))

MPLAYER_PID=""; AUDIO_PID=""; CAM_PID=""

# ── Subida a YouTube ─────────────────────────────────────────
upload_youtube() {
    local VIDEO_PATH="$1"

    echo ""
    read -r -p "¿Subir a YouTube? [s/N]: " RESPUESTA
    if [[ ! "$RESPUESTA" =~ ^[sS]$ ]]; then
        echo "Listo. Video guardado en: $VIDEO_PATH"
        return
    fi

    read -r -p "Título: " YT_TITLE
    read -r -p "Descripción: " YT_DESC

    echo "Privacidad:"
    echo "  1) Público"
    echo "  2) No listado"
    echo "  3) Privado"
    read -r -p "Elegí [1-3] (default: 2): " YT_PRIV_OPT
    case "$YT_PRIV_OPT" in
        1) YT_PRIVACY="public"   ;;
        3) YT_PRIVACY="private"  ;;
        *) YT_PRIVACY="unlisted" ;;
    esac

    echo ""
    echo "Subiendo '$YT_TITLE' como [$YT_PRIVACY]..."
    source "$HOME/youtube-upload/bin/activate"
    "$HOME/youtube-upload/youtube-upload/bin/youtube-upload" \
        --title="$YT_TITLE" \
        --description="$YT_DESC" \
        --recording-date="$(date +%Y-%m-%dT%H:%M:%S.0Z)" \
        --privacy="$YT_PRIVACY" \
        --embeddable=True \
        "$VIDEO_PATH"

    if [ $? -eq 0 ]; then
        echo "✓ Video subido exitosamente."
    else
        echo "✗ Error al subir. El video sigue en: $VIDEO_PATH"
    fi
}

cleanup() {
    [ -n "$MPLAYER_PID" ] && kill "$MPLAYER_PID" 2>/dev/null
    [ -n "$AUDIO_PID" ]   && kill "$AUDIO_PID"   2>/dev/null
    [ -n "$CAM_PID" ]     && kill "$CAM_PID"      2>/dev/null
    wait 2>/dev/null

    if [ -f "$VIDEO_FILE" ] && [ -f "$CAM_FILE" ] && [ -f "$AUDIO_FILE" ]; then
        echo "Combinando video, cámara y audio..."
        ffmpeg \
            -i "$VIDEO_FILE" \
            -i "$CAM_FILE" \
            -i "$AUDIO_FILE" \
            -filter_complex "
                [0:v]scale=1080:1350,setsar=1[screen];
                [1:v]scale=1080:570,
                    unsharp=3:3:0.8:3:3:0,
                    eq=brightness=0.02:contrast=1.05:saturation=1.1,
                    setsar=1[cam];
                [screen][cam]vstack=inputs=2[stacked];
                [stacked]drawbox=x=0:y=0:w=iw:h=ih:color=red@0.9:t=18[framed];
                [framed]drawbox=x=8:y=8:w=iw-16:h=ih-16:color=red@0.5:t=4,
                drawtext=text='● REC':fontcolor=red:fontsize=60:x=30:y=30:
                    fontfile=/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf[out]
            " \
            -map "[out]" -map "2:a" \
            -c:v libx264 -preset ultrafast -crf 28 \
            -c:a aac -b:a 128k -shortest \
            "$OUTPUT_FILE" -y
        rm -f "$AUDIO_FILE" "$VIDEO_FILE" "$CAM_FILE"
        echo "Guardado en: $OUTPUT_FILE"

        upload_youtube "$OUTPUT_FILE"
    fi
    exit 0
}

trap 'cleanup' EXIT INT TERM

# ── Preview cámara ───────────────────────────────────────────
mplayer -vo fbdev2 \
    -tv driver=v4l2:device=/dev/video0:width=640:height=480 tv:// \
    -zoom -geometry 90%:90% &
MPLAYER_PID=$!

# ── Grabar cámara ────────────────────────────────────────────
ffmpeg \
    -f v4l2 \
    -video_size 640x480 \
    -framerate 30 \
    -i /dev/video0 \
    -vf "unsharp=3:3:0.5:3:3:0,eq=brightness=0.02:contrast=1.03:saturation=1.08" \
    -c:v libx264 -preset ultrafast -crf 23 \
    "$CAM_FILE" -y &
CAM_PID=$!

# ── Grabar audio ─────────────────────────────────────────────
MONITOR=$(pactl list sources short | grep monitor | head -1 | awk '{print $2}')
ffmpeg -f pulse -i "$MONITOR" -f pulse -i default \
    -filter_complex "[0:a][1:a]amix=inputs=2[aout]" \
    -map "[aout]" "$AUDIO_FILE" -y &
AUDIO_PID=$!

# ── Grabar pantalla (pipeline CPU puro — sin mezcla hwaccel/software) ────────
# FIX: El pipeline anterior mezclaba hwdownload con scale_vaapi al final,
# lo que causaba stalls y frames trabados al decodificar en el navegador.
# Solución: bajar a CPU con hwdownload, hacer crop+scale en software, y
# encodear con libx264 ultrafast para mantener framerate estable.
sudo LIBVA_DRIVER_NAME=i965 ffmpeg \
    -f kmsgrab -framerate 30 -device /dev/dri/card1 -i - \
    -vaapi_device /dev/dri/renderD128 \
    -vf "hwmap=derive_device=vaapi,\
scale_vaapi=w=${SCREEN_W}:h=${SCREEN_H}:format=nv12,\
hwdownload,format=nv12,\
crop=${CROP_W}:${CROP_H}:${CROP_X}:${CROP_Y},\
scale=1080:1350" \
    -c:v libx264 -preset ultrafast -crf 23 \
    -g 30 -keyint_min 30 \
    -x264-params "nal-hrd=cbr:force-cfr=1" \
    -b:v 4M -maxrate 4M -bufsize 2M \
    "$VIDEO_FILE" -y

alias mutemic=‘pactl set-source-mute @DEFAULT_SOURCE@ 1 && echo “🔇 Micro muteado”’ alias unmutemic=‘pactl set-source-mute @DEFAULT_SOURCE@ 0 && echo “🎙️ Micro activo”’ alias togglemic=‘pactl set-source-mute @DEFAULT_SOURCE@ toggle && echo “🎙️ Micro toggled”’

mplayer -vo fbdev2 -vf scale=-2:340 -geometry 0:0 *mp4

# ~/.tmux.conf
set -g default-terminal "screen-256color"
setw -g aggressive-resize off
set -g status-style "bg=black,fg=green"
set -g status-left ""
set -g status-right " arch install "
set -g status-justify centre

# forzar tamaño en cada cliente que se conecta
set-hook -g client-attached      'resize-window -x 38 -y 24'
set-hook -g client-session-changed 'resize-window -x 38 -y 24'

tmux new-session -s record -x 38 -y 24

Entonces el video tiene que ser 608×768px exacto.

Si el video original es 16:9 (típico), escalado a 608 de ancho:

608 × 9/16 = 342px alto

Para llenarlo verticalmente escalás al alto y croppeás los costados:

768px alto → ancho proporcional 768 × 16/9 = 1365px
crop al centro: (1365 - 608) / 2 = 378px de cada lado

El comando

mplayer -vo fbdev2 \
    -vf scale=1366:768,crop=608:768:379:0 \
    *.mp4

Si el video es vertical (9:16):

mplayer -vo fbdev2 \
    -vf scale=608:768 \
    *.mp4

Si el video es cuadrado:

mplayer -vo fbdev2 \
    -vf scale=608:608,expand=608:768:0:80 \
    *.mp4

Probá primero sin crop para ver qué tamaño tiene tu video

mplayer -identify -frames 0 *.mp4 2>/dev/null | grep VIDEO

mplayer -vo fbdev2 -vf scale=1366:768,crop=608:768:379:0 -geometry 0%:0% *mp4

Horizontal :)

#!/bin/bash
# Uso: ./own_obs.sh [x y w h]
# Sin argumentos: captura pantalla completa 1366x768 (16:9)
# Con argumentos:  ./own_obs.sh 0 0 1366 768
#
# Extras disponibles (variables al inicio):
#   SHOW_REC=1         → indicador ● REC
#   CAM_ENABLED=1      → overlay de cámara (0 para desactivar)
#   CAM_SIZE=320       → ancho del overlay de cámara en px
#   CAM_SHAPE=round    → "round" para cámara circular, "rect" para rectangular
#   INTRO_TEXT=""      → texto de intro que aparece los primeros 3 seg
#   WATERMARK=""       → ruta a imagen PNG para watermark (ej: ~/logo.png)

set -euo pipefail

# ════════════════════════════════════════════════════
#  CONFIGURACIÓN — editá estas variables
# ════════════════════════════════════════════════════
SHOW_REC=1
CAM_ENABLED=1
CAM_SIZE=280          # ancho del recuadro de cámara en px
CAM_SHAPE=round       # "round" o "rect"
INTRO_TEXT=""         # ej: "Hola, bienvenidos al canal"
WATERMARK=""          # ruta a logo PNG con alpha, o vacío para omitir
FONT=/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf

SCREEN_W=1366
SCREEN_H=768

# Salida 16:9 — YouTube 1080p estándar
OUT_W=1280
OUT_H=720

# Cámara: forzamos 16:9 en captura para no distorsionar
CAM_INPUT_W=640
CAM_INPUT_H=360

# ════════════════════════════════════════════════════
#  ARCHIVOS TEMPORALES
# ════════════════════════════════════════════════════
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
AUDIO_FILE="$HOME/audio_${TIMESTAMP}.wav"
VIDEO_FILE="$HOME/recording_${TIMESTAMP}.mp4"
CAM_FILE="$HOME/cam_${TIMESTAMP}.mp4"
OUTPUT_FILE="$HOME/final_${TIMESTAMP}.mp4"

# ════════════════════════════════════════════════════
#  REGIÓN A CAPTURAR
# ════════════════════════════════════════════════════
if [ $# -eq 4 ]; then
    CROP_X=$1; CROP_Y=$2; CROP_W=$3; CROP_H=$4
    echo "Región manual: ${CROP_W}x${CROP_H} en offset ${CROP_X},${CROP_Y}"
else
    CROP_X=0; CROP_Y=0
    CROP_W=$SCREEN_W; CROP_H=$SCREEN_H
    echo "Captura completa: ${CROP_W}x${CROP_H}"
fi
CROP_W=$(( CROP_W & ~1 ))
CROP_H=$(( CROP_H & ~1 ))

# ════════════════════════════════════════════════════
#  PIDS
# ════════════════════════════════════════════════════
MPLAYER_PID=""; AUDIO_PID=""; CAM_PID=""

# ════════════════════════════════════════════════════
#  SUBIDA A YOUTUBE
# ════════════════════════════════════════════════════
upload_youtube() {
    local VIDEO_PATH="$1"
    echo ""
    read -r -p "¿Subir a YouTube? [s/N]: " RESPUESTA
    if [[ ! "$RESPUESTA" =~ ^[sS]$ ]]; then
        echo "Listo. Video guardado en: $VIDEO_PATH"
        return
    fi

    read -r -p "Título: " YT_TITLE
    read -r -p "Descripción (Enter para vacía): " YT_DESC

    echo "Privacidad:"
    echo "  1) Público"
    echo "  2) No listado"
    echo "  3) Privado"
    read -r -p "Elegí [1-3] (default: 2): " YT_PRIV_OPT
    case "$YT_PRIV_OPT" in
        1) YT_PRIVACY="public"   ;;
        3) YT_PRIVACY="private"  ;;
        *) YT_PRIVACY="unlisted" ;;
    esac

    echo ""
    echo "Subiendo '$YT_TITLE' como [$YT_PRIVACY]..."
    source "$HOME/youtube-upload/bin/activate"
    "$HOME/youtube-upload/youtube-upload/bin/youtube-upload" \
        --title="$YT_TITLE" \
        --description="$YT_DESC" \
        --recording-date="$(date +%Y-%m-%dT%H:%M:%S.0Z)" \
        --privacy="$YT_PRIVACY" \
        --embeddable=True \
        "$VIDEO_PATH"

    if [ $? -eq 0 ]; then
        echo "✓ Video subido exitosamente."
    else
        echo "✗ Error al subir. El video sigue en: $VIDEO_PATH"
    fi
}

# ════════════════════════════════════════════════════
#  CLEANUP + COMPOSICIÓN FINAL
# ════════════════════════════════════════════════════
cleanup() {
    [ -n "$MPLAYER_PID" ] && kill "$MPLAYER_PID" 2>/dev/null || true
    [ -n "$AUDIO_PID" ]   && kill "$AUDIO_PID"   2>/dev/null || true
    [ -n "$CAM_PID" ]     && kill "$CAM_PID"      2>/dev/null || true
    wait 2>/dev/null || true

    # ── Verificar que existan los archivos necesarios ────────
    MISSING=0
    [ ! -f "$VIDEO_FILE" ] && echo "✗ Falta video de pantalla" && MISSING=1
    [ ! -f "$AUDIO_FILE" ] && echo "✗ Falta audio"             && MISSING=1
    if [ "$CAM_ENABLED" = "1" ] && [ ! -f "$CAM_FILE" ]; then
        echo "✗ Falta video de cámara" && MISSING=1
    fi
    [ "$MISSING" = "1" ] && exit 1

    echo ""
    echo "══════════════════════════════════════"
    echo " Componiendo video final..."
    echo "══════════════════════════════════════"

    # ── Construir filter_complex dinámicamente ───────────────
    # La lógica es:
    #   [0:v] → pantalla escalada → [screen]
    #   [1:v] → cámara escalada  → [cam_scaled]
    #            si CAM_SHAPE=round → máscara circular → [cam_masked]
    #   overlay de cámara en esquina inferior derecha
    #   luego capas de texto: REC, reloj, intro, watermark

    CAM_H=$(( CAM_SIZE * CAM_INPUT_H / CAM_INPUT_W ))
    CAM_H=$(( CAM_H & ~1 ))
    # padding de 20px desde los bordes
    CAM_PAD=20
    CAM_X=$(( OUT_W - CAM_SIZE - CAM_PAD ))
    CAM_Y=$(( OUT_H - CAM_H   - CAM_PAD ))

    # ── Inputs de ffmpeg ─────────────────────────────────────
    FFMPEG_INPUTS="-i $VIDEO_FILE"
    if [ "$CAM_ENABLED" = "1" ]; then
        FFMPEG_INPUTS="$FFMPEG_INPUTS -i $CAM_FILE"
    fi
    FFMPEG_INPUTS="$FFMPEG_INPUTS -i $AUDIO_FILE"
    AUDIO_IDX=2
    [ "$CAM_ENABLED" != "1" ] && AUDIO_IDX=1

    # ── filter_complex ───────────────────────────────────────
    FC="[0:v]scale=${OUT_W}:${OUT_H},setsar=1,format=yuva420p[screen];"

    if [ "$CAM_ENABLED" = "1" ]; then
        FC+="[1:v]scale=${CAM_SIZE}:${CAM_H},setsar=1,format=yuva420p[cam_scaled];"

        if [ "$CAM_SHAPE" = "round" ]; then
            # Máscara circular usando geq: pinta alpha=0 fuera del círculo
            R=$(( CAM_SIZE / 2 ))
            FC+="[cam_scaled]geq=\
lum='lum(X,Y)':\
a='if(lte((X-${R})^2+(Y-${R})^2,${R}^2),255,0)'[cam_masked];"
            CAM_OUT="cam_masked"
        else
            CAM_OUT="cam_scaled"
        fi

        # Borde/sombra alrededor de la cámara
        if [ "$CAM_SHAPE" = "round" ]; then
            # Para cámara redonda el borde se pone sobre el overlay ya compuesto
            BORDER_FILTER="drawbox=x=${CAM_X}:y=${CAM_Y}:w=${CAM_SIZE}:h=${CAM_H}:color=black@0.0:t=0,"
        else
            BORDER_FILTER="drawbox=x=$(( CAM_X - 3 )):y=$(( CAM_Y - 3 )):w=$(( CAM_SIZE + 6 )):h=$(( CAM_H + 6 )):color=white@0.8:t=3,"
        fi

        FC+="[screen][${CAM_OUT}]overlay=x=${CAM_X}:y=${CAM_Y}:format=auto[with_cam];"
        LAST="with_cam"
    else
        LAST="screen"
    fi

    # ── Capas de texto y decoración ──────────────────────────
    TEXT_CHAIN="[${LAST}]"

    # Indicador REC
    if [ "$SHOW_REC" = "1" ]; then
        REC_FILE=$(mktemp /tmp/rec_XXXXXX.txt)
        printf '● REC' > "$REC_FILE"
        TEXT_CHAIN+="drawbox=x=12:y=12:w=130:h=44:color=black@0.45:t=fill,"
        TEXT_CHAIN+="drawtext=textfile=${REC_FILE}:fontcolor=red:fontsize=28:x=20:y=20:fontfile=${FONT},"
    fi

    # Intro text: aparece los primeros 3 segundos con fade out
    if [ -n "$INTRO_TEXT" ]; then
        TEXT_CHAIN+="drawtext=text=${INTRO_TEXT}:fontcolor=white:fontsize=42:"
        TEXT_CHAIN+="x=(w-tw)/2:y=(h-th)/2:"
        TEXT_CHAIN+="fontfile=${FONT}:"
        TEXT_CHAIN+="shadowcolor=black:shadowx=3:shadowy=3:"
        TEXT_CHAIN+="enable=between(t\,0\,3):"
        TEXT_CHAIN+="alpha=if(lt(t\,2.5)\,1\,max(0\,1-(t-2.5)/0.5)),"
    fi

    # Quitar la coma final y cerrar con [out]
    TEXT_CHAIN="${TEXT_CHAIN%,}[out]"
    FC+="${TEXT_CHAIN}"

    # Watermark (imagen PNG con alpha) — se agrega como input extra si se especifica
    WM_INPUT=""
    WM_EXTRA=""
    if [ -n "$WATERMARK" ] && [ -f "$WATERMARK" ]; then
        WM_INPUT="-i $WATERMARK"
        # Reemplazar [out] final por composición con watermark
        FC="${FC%\[out\]}"
        WM_IDX=$(( AUDIO_IDX + 0 ))   # viene después del audio
        WM_INPUT="-i $WATERMARK"
        FC+=";[out_pre][${WM_IDX}:v]overlay=x=w-overlay_w-20:y=h-overlay_h-20[out]"
        AUDIO_IDX=$(( AUDIO_IDX + 1 ))
    fi

    # ── Ejecutar ffmpeg final ─────────────────────────────────
    ffmpeg \
        $FFMPEG_INPUTS \
        $WM_INPUT \
        -filter_complex "$FC" \
        -map "[out]" -map "${AUDIO_IDX}:a" \
        -c:v libx264 -preset fast -crf 23 \
        -c:a aac -b:a 128k \
        -movflags +faststart \
        -shortest \
        "$OUTPUT_FILE" -y

    rm -f "$AUDIO_FILE" "$VIDEO_FILE" "$CAM_FILE" /tmp/rec_*.txt

    echo ""
    echo "══════════════════════════════════════"
    echo " ✓ Guardado en: $OUTPUT_FILE"
    SIZE=$(du -h "$OUTPUT_FILE" | cut -f1)
    DUR=$(ffprobe -v error -show_entries format=duration \
          -of default=noprint_wrappers=1:nokey=1 "$OUTPUT_FILE" 2>/dev/null \
          | awk '{printf "%02d:%02d", $1/60, $1%60}')
    echo " Tamaño: $SIZE   Duración: ${DUR}"
    echo "══════════════════════════════════════"

    upload_youtube "$OUTPUT_FILE"
    exit 0
}

trap 'cleanup' EXIT INT TERM

# ════════════════════════════════════════════════════
#  INICIAR GRABACIONES
# ════════════════════════════════════════════════════

# ── Configurar cámara ────────────────────────────────────────
v4l2-ctl --device=/dev/video0 \
    --set-fmt-video=width=${CAM_INPUT_W},height=${CAM_INPUT_H},pixelformat=YUYV

# ── Preview cámara en framebuffer ────────────────────────────
if [ "$CAM_ENABLED" = "1" ]; then
    mplayer -vo fbdev2 \
        -tv driver=v4l2:device=/dev/video0:width=${CAM_INPUT_W}:height=${CAM_INPUT_H} tv:// \
        -zoom -geometry 95%:95% &
    MPLAYER_PID=$!
fi

# ── Grabar cámara ────────────────────────────────────────────
if [ "$CAM_ENABLED" = "1" ]; then
    ffmpeg -f v4l2 \
        -video_size ${CAM_INPUT_W}x${CAM_INPUT_H} \
        -framerate 30 \
        -i /dev/video0 \
        -c:v libx264 -preset ultrafast -crf 26 \
        -tune zerolatency \
        "$CAM_FILE" -y &
    CAM_PID=$!
fi

# ── Grabar audio (monitor del sistema + micrófono mezclados) ─
MONITOR=$(pactl list sources short | grep monitor | head -1 | awk '{print $2}')
MIC=$(pactl list sources short | grep -v monitor | head -1 | awk '{print $2}')
echo "Monitor: $MONITOR"
echo "Micrófono: $MIC"
ffmpeg \
    -f pulse -i "$MONITOR" \
    -f pulse -i "$MIC" \
    -filter_complex "[0:a][1:a]amix=inputs=2:duration=longest:dropout_transition=3[aout]" \
    -map "[aout]" \
    -acodec pcm_s16le \
    "$AUDIO_FILE" -y &
AUDIO_PID=$!

# ── Grabar pantalla vía kmsgrab + VAAPI ──────────────────────
echo ""
echo "══════════════════════════════════════"
echo " ● Grabando — Ctrl+C para detener"
echo "══════════════════════════════════════"

sudo LIBVA_DRIVER_NAME=i965 ffmpeg \
    -f kmsgrab -framerate 30 -device /dev/dri/card1 -i - \
    -vaapi_device /dev/dri/renderD128 \
    -vf "hwmap=derive_device=vaapi,\
scale_vaapi=w=${SCREEN_W}:h=${SCREEN_H}:format=nv12,\
hwdownload,format=nv12,\
crop=${CROP_W}:${CROP_H}:${CROP_X}:${CROP_Y},\
scale=${OUT_W}:${OUT_H},\
hwupload,scale_vaapi=format=nv12" \
    -c:v h264_vaapi -global_quality 26 \
    "$VIDEO_FILE" -y

Nueva version sin efectos (no lo probe)

#!/bin/bash
# Uso: ./own_obs.sh [x y w h]
# Sin argumentos: captura pantalla completa 1366x768 (16:9)
# Con argumentos:  ./own_obs.sh 0 0 1366 768
#
# Extras disponibles (variables al inicio):
#   SHOW_REC=1         → indicador ● REC
#   CAM_ENABLED=1      → overlay de cámara (0 para desactivar)
#   CAM_SIZE=320       → ancho del overlay de cámara en px
#   INTRO_TEXT=""      → texto de intro que aparece los primeros 3 seg
#   WATERMARK=""       → ruta a imagen PNG para watermark (ej: ~/logo.png)

set -euo pipefail

# ════════════════════════════════════════════════════
#  CONFIGURACIÓN — editá estas variables
# ════════════════════════════════════════════════════
SHOW_REC=1
CAM_ENABLED=1
CAM_SIZE=280          # ancho del recuadro de cámara en px
INTRO_TEXT=""         # ej: "Hola, bienvenidos al canal"
WATERMARK=""          # ruta a logo PNG con alpha, o vacío para omitir
FONT=/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf

SCREEN_W=1366
SCREEN_H=768

# Salida 16:9 — YouTube 1080p estándar
OUT_W=1280
OUT_H=720

# Cámara: forzamos 16:9 en captura para no distorsionar
CAM_INPUT_W=640
CAM_INPUT_H=360

# ════════════════════════════════════════════════════
#  ARCHIVOS TEMPORALES
# ════════════════════════════════════════════════════
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
AUDIO_FILE="$HOME/audio_${TIMESTAMP}.wav"
VIDEO_FILE="$HOME/recording_${TIMESTAMP}.mp4"
CAM_FILE="$HOME/cam_${TIMESTAMP}.mp4"
OUTPUT_FILE="$HOME/final_${TIMESTAMP}.mp4"

# ════════════════════════════════════════════════════
#  REGIÓN A CAPTURAR
# ════════════════════════════════════════════════════
if [ $# -eq 4 ]; then
    CROP_X=$1; CROP_Y=$2; CROP_W=$3; CROP_H=$4
    echo "Región manual: ${CROP_W}x${CROP_H} en offset ${CROP_X},${CROP_Y}"
else
    CROP_X=0; CROP_Y=0
    CROP_W=$SCREEN_W; CROP_H=$SCREEN_H
    echo "Captura completa: ${CROP_W}x${CROP_H}"
fi
CROP_W=$(( CROP_W & ~1 ))
CROP_H=$(( CROP_H & ~1 ))

# ════════════════════════════════════════════════════
#  PIDS
# ════════════════════════════════════════════════════
MPLAYER_PID=""; AUDIO_PID=""; CAM_PID=""

# ════════════════════════════════════════════════════
#  SUBIDA A YOUTUBE
# ════════════════════════════════════════════════════
upload_youtube() {
    local VIDEO_PATH="$1"
    echo ""
    read -r -p "¿Subir a YouTube? [s/N]: " RESPUESTA
    if [[ ! "$RESPUESTA" =~ ^[sS]$ ]]; then
        echo "Listo. Video guardado en: $VIDEO_PATH"
        return
    fi

    read -r -p "Título: " YT_TITLE
    read -r -p "Descripción (Enter para vacía): " YT_DESC

    echo "Privacidad:"
    echo "  1) Público"
    echo "  2) No listado"
    echo "  3) Privado"
    read -r -p "Elegí [1-3] (default: 2): " YT_PRIV_OPT
    case "$YT_PRIV_OPT" in
        1) YT_PRIVACY="public"   ;;
        3) YT_PRIVACY="private"  ;;
        *) YT_PRIVACY="unlisted" ;;
    esac

    echo ""
    echo "Subiendo '$YT_TITLE' como [$YT_PRIVACY]..."
    source "$HOME/youtube-upload/bin/activate"
    "$HOME/youtube-upload/youtube-upload/bin/youtube-upload" \
        --title="$YT_TITLE" \
        --description="$YT_DESC" \
        --recording-date="$(date +%Y-%m-%dT%H:%M:%S.0Z)" \
        --privacy="$YT_PRIVACY" \
        --embeddable=True \
        "$VIDEO_PATH"

    if [ $? -eq 0 ]; then
        echo "✓ Video subido exitosamente."
    else
        echo "✗ Error al subir. El video sigue en: $VIDEO_PATH"
    fi
}

# ════════════════════════════════════════════════════
#  CLEANUP + COMPOSICIÓN FINAL
# ════════════════════════════════════════════════════
cleanup() {
    [ -n "$MPLAYER_PID" ] && kill "$MPLAYER_PID" 2>/dev/null || true
    [ -n "$AUDIO_PID" ]   && kill "$AUDIO_PID"   2>/dev/null || true
    [ -n "$CAM_PID" ]     && kill "$CAM_PID"      2>/dev/null || true
    wait 2>/dev/null || true

    # ── Verificar que existan los archivos necesarios ────────
    MISSING=0
    [ ! -f "$VIDEO_FILE" ] && echo "✗ Falta video de pantalla" && MISSING=1
    [ ! -f "$AUDIO_FILE" ] && echo "✗ Falta audio"             && MISSING=1
    if [ "$CAM_ENABLED" = "1" ] && [ ! -f "$CAM_FILE" ]; then
        echo "✗ Falta video de cámara" && MISSING=1
    fi
    [ "$MISSING" = "1" ] && exit 1

    echo ""
    echo "══════════════════════════════════════"
    echo " Componiendo video final..."
    echo "══════════════════════════════════════"

    # ── Construir filter_complex dinámicamente ───────────────
    # La lógica es:
    #   [0:v] → pantalla escalada → [screen]
    #   [1:v] → cámara escalada  → [cam_scaled]
    #            si CAM_SHAPE=round → máscara circular → [cam_masked]
    #   overlay de cámara en esquina inferior derecha
    #   luego capas de texto: REC, reloj, intro, watermark

    CAM_H=$(( CAM_SIZE * CAM_INPUT_H / CAM_INPUT_W ))
    CAM_H=$(( CAM_H & ~1 ))
    # padding de 20px desde los bordes
    CAM_PAD=20
    CAM_X=$(( OUT_W - CAM_SIZE - CAM_PAD ))
    CAM_Y=$(( OUT_H - CAM_H   - CAM_PAD ))

    # ── Inputs de ffmpeg ─────────────────────────────────────
    FFMPEG_INPUTS="-i $VIDEO_FILE"
    if [ "$CAM_ENABLED" = "1" ]; then
        FFMPEG_INPUTS="$FFMPEG_INPUTS -i $CAM_FILE"
    fi
    FFMPEG_INPUTS="$FFMPEG_INPUTS -i $AUDIO_FILE"
    AUDIO_IDX=2
    [ "$CAM_ENABLED" != "1" ] && AUDIO_IDX=1

    # ── filter_complex ───────────────────────────────────────
    FC="[0:v]scale=${OUT_W}:${OUT_H},setsar=1[screen];"

    if [ "$CAM_ENABLED" = "1" ]; then
        FC+="[1:v]scale=${CAM_SIZE}:${CAM_H},setsar=1[cam_scaled];"

        FC+="[screen][cam_scaled]overlay=x=${CAM_X}:y=${CAM_Y}[with_cam];"
        LAST="with_cam"
    else
        LAST="screen"
    fi

    # ── Capas de texto y decoración ──────────────────────────
    TEXT_CHAIN="[${LAST}]"

    # Indicador REC
    if [ "$SHOW_REC" = "1" ]; then
        REC_FILE=$(mktemp /tmp/rec_XXXXXX.txt)
        printf '● REC' > "$REC_FILE"
        TEXT_CHAIN+="drawbox=x=12:y=12:w=130:h=44:color=black@0.45:t=fill,"
        TEXT_CHAIN+="drawtext=textfile=${REC_FILE}:fontcolor=red:fontsize=28:x=20:y=20:fontfile=${FONT},"
    fi

    # Intro text: aparece los primeros 3 segundos con fade out
    if [ -n "$INTRO_TEXT" ]; then
        TEXT_CHAIN+="drawtext=text=${INTRO_TEXT}:fontcolor=white:fontsize=42:"
        TEXT_CHAIN+="x=(w-tw)/2:y=(h-th)/2:"
        TEXT_CHAIN+="fontfile=${FONT}:"
        TEXT_CHAIN+="shadowcolor=black:shadowx=3:shadowy=3:"
        TEXT_CHAIN+="enable=between(t\,0\,3):"
        TEXT_CHAIN+="alpha=if(lt(t\,2.5)\,1\,max(0\,1-(t-2.5)/0.5)),"
    fi

    # Quitar la coma final y cerrar con [out]
    TEXT_CHAIN="${TEXT_CHAIN%,}[out]"
    FC+="${TEXT_CHAIN}"

    # Watermark (imagen PNG con alpha) — se agrega como input extra si se especifica
    WM_INPUT=""
    WM_EXTRA=""
    if [ -n "$WATERMARK" ] && [ -f "$WATERMARK" ]; then
        WM_INPUT="-i $WATERMARK"
        # Reemplazar [out] final por composición con watermark
        FC="${FC%\[out\]}"
        WM_IDX=$(( AUDIO_IDX + 0 ))   # viene después del audio
        WM_INPUT="-i $WATERMARK"
        FC+=";[out_pre][${WM_IDX}:v]overlay=x=w-overlay_w-20:y=h-overlay_h-20[out]"
        AUDIO_IDX=$(( AUDIO_IDX + 1 ))
    fi

    # ── Ejecutar ffmpeg final ─────────────────────────────────
    ffmpeg \
        $FFMPEG_INPUTS \
        $WM_INPUT \
        -filter_complex "$FC" \
        -map "[out]" -map "${AUDIO_IDX}:a" \
        -c:v libx264 -preset fast -crf 23 \
        -c:a aac -b:a 128k \
        -movflags +faststart \
        -shortest \
        "$OUTPUT_FILE" -y

    rm -f "$AUDIO_FILE" "$VIDEO_FILE" "$CAM_FILE" /tmp/rec_*.txt

    echo ""
    echo "══════════════════════════════════════"
    echo " ✓ Guardado en: $OUTPUT_FILE"
    SIZE=$(du -h "$OUTPUT_FILE" | cut -f1)
    DUR=$(ffprobe -v error -show_entries format=duration \
          -of default=noprint_wrappers=1:nokey=1 "$OUTPUT_FILE" 2>/dev/null \
          | awk '{printf "%02d:%02d", $1/60, $1%60}')
    echo " Tamaño: $SIZE   Duración: ${DUR}"
    echo "══════════════════════════════════════"

    upload_youtube "$OUTPUT_FILE"
    exit 0
}

trap 'cleanup' EXIT INT TERM

# ════════════════════════════════════════════════════
#  INICIAR GRABACIONES
# ════════════════════════════════════════════════════

# ── Configurar cámara ────────────────────────────────────────
v4l2-ctl --device=/dev/video0 \
    --set-fmt-video=width=${CAM_INPUT_W},height=${CAM_INPUT_H},pixelformat=YUYV

# ── Preview cámara en framebuffer ────────────────────────────
if [ "$CAM_ENABLED" = "1" ]; then
    mplayer -vo fbdev2 \
        -tv driver=v4l2:device=/dev/video0:width=${CAM_INPUT_W}:height=${CAM_INPUT_H} tv:// \
        -zoom -geometry 95%:95% &
    MPLAYER_PID=$!
fi

# ── Grabar cámara ────────────────────────────────────────────
if [ "$CAM_ENABLED" = "1" ]; then
    ffmpeg -f v4l2 \
        -video_size ${CAM_INPUT_W}x${CAM_INPUT_H} \
        -framerate 30 \
        -i /dev/video0 \
        -c:v libx264 -preset ultrafast -crf 26 \
        -tune zerolatency \
        "$CAM_FILE" -y &
    CAM_PID=$!
fi

# ── Grabar audio (monitor del sistema + micrófono mezclados) ─
MONITOR=$(pactl list sources short | grep monitor | head -1 | awk '{print $2}')
MIC=$(pactl list sources short | grep -v monitor | head -1 | awk '{print $2}')
echo "Monitor: $MONITOR"
echo "Micrófono: $MIC"
ffmpeg \
    -f pulse -i "$MONITOR" \
    -f pulse -i "$MIC" \
    -filter_complex "[0:a][1:a]amix=inputs=2:duration=longest:dropout_transition=3[aout]" \
    -map "[aout]" \
    -acodec pcm_s16le \
    "$AUDIO_FILE" -y &
AUDIO_PID=$!

# ── Grabar pantalla vía kmsgrab + VAAPI ──────────────────────
echo ""
echo "══════════════════════════════════════"
echo " ● Grabando — Ctrl+C para detener"
echo "══════════════════════════════════════"

sudo LIBVA_DRIVER_NAME=i965 ffmpeg \
    -f kmsgrab -framerate 30 -device /dev/dri/card1 -i - \
    -vaapi_device /dev/dri/renderD128 \
    -vf "hwmap=derive_device=vaapi,\
scale_vaapi=w=${SCREEN_W}:h=${SCREEN_H}:format=nv12,\
hwdownload,format=nv12,\
crop=${CROP_W}:${CROP_H}:${CROP_X}:${CROP_Y},\
scale=${OUT_W}:${OUT_H},\
hwupload,scale_vaapi=format=nv12" \
    -c:v h264_vaapi -global_quality 26 \
    "$VIDEO_FILE" -y