pular pro conteúdo
NO AR 00:00:00:00 compartilhar a tela em grupo · linux + windows v3.10.2

Compartilhe a tela com a galera e assista à deles com atraso baixo de verdade. Qualquer um transmite, todo mundo assiste, fala e conversa. Sem conta e sem anúncio, e no Windows nem precisa instalar.

v3.10.2 · todas as versões e arquivos · código aberto (MIT)
17ms
de ponta a ponta, em rede local
20%
de um núcleo pra 1080p60 em HEVC
0
contas, anúncios ou rastreadores
connect.zig — telinha
  • telinha
  • src
  • capture_linux.c
  • connect.zig
  • server.zig
  • theme.zig
  • ui.zig
  • bots
  • README.md
264pub fn race(targets: []const Target, version: u16, timeout_ms: u32, cancel: *const std.atomic.Value(bool), fail: *Failure) Error!Winner {265  if (targets.len == 0) return error.UnknownHost;266  const n = @min(targets.len, max_targets);267  const r = gpa.create(Race) catch return error.ThreadFailed;268  r.* = .{ .refs = .init(1), .total = n, .targets = undefined };269  @memcpy(r.targets[0..n], targets[0..n]);270  for (0..n) |i| {271    _ = r.refs.fetchAdd(1, .monotonic);272    sync.spawn(attempt, .{ r, i, @as(u32, @intCast(i)) * 250, version }) catch {273      _ = r.refs.fetchSub(1, .monotonic);274      r.mutex.lock();275      r.finished += 1;276      r.mutex.unlock();277    };278  }279  defer r.release();280  const t0 = sync.nowMs();281  r.mutex.lock();282  defer r.mutex.unlock();283  while (r.winner == null and r.finished < r.total) {284    if (cancel.load(.acquire) or sync.nowMs() - t0 > timeout_ms) {285      r.cancel.store(true, .release);286      fail.err = if (cancel.load(.acquire)) error.Cancelled else error.ConnectFailed;287      return fail.err;288    }289    r.cond.timedWait(&r.mutex, 50);290  }291  if (r.winner) |w| return w;292  fail.err = if (r.version_mismatch) |v| blk: {293    fail.server_version = v;294    break :blk error.VersionMismatch;295  } else if (r.not_telinha) error.NotTelinha else if (r.refused) error.ConnectionRefused else if (r.unknown and r.total == 1) error.UnknownHost else error.ConnectFailed;296  return fail.err;297}
mainZig 0.16UTF-8LF
telinha1920×1080 · 60
~/telinha — zsh

    
in #geral · 4
AAnaLIVE
BBruno
CCarol
MMúsicaBOT
TOCANDO AGORA Soma FM — Groove Salad pedido da Carol
⏸⏭fila
Mensagem em #geral

Simulado no seu navegador: nada aqui é transmissão de verdade. Clique numa tela pequena pra cortar pra ela.

Teste ao vivo

Sinta os 17 ms

Risque a sua tela. A tela do seu amigo recebe com o atraso que você escolher, e aí dá pra sentir o que aquele número quer dizer.

Sua tela
risque aqui
Tela do seu amigo17 ms
atraso
  1. CapturaDMA-BUF ou DXGI, direto do compositor
  2. CodificaNVENC, 8 ms
  3. EnviaUDP, reenviando só o que se perdeu
  4. DecodificaNVDEC, 6 ms
  5. Mostradireto numa textura OpenGL

Medido num i5-2400S de 2011 com GTX 1050 Ti, em 1080p60 e HEVC, na rede local. Pela internet, some a distância entre vocês. Neste teste, o seu navegador e o seu monitor somam o atraso deles por cima. Como o caminho funciona →

Boletim

Novidades

O que entrou na última versão.

  • Surdo cala só a call: o som das transmissões que você está assistindo continua tocando (antes o surdo calava tudo, até o vídeo do amigo). Os avisos sonoros também continuam.
Espelho

Por que o telinha

  1. 01

    Rápido

    Captura, codificação e decodificação ficam na GPU. O atraso é medido a cada quadro, e a transmissão se ajusta antes de ele acumular.

  2. 02

    Um convite e pronto

    O convite leva todos os caminhos até a sala: rede local, porta do roteador, IPv6, link público. Cada um entra pelo mais rápido que funcionar.

  3. 03

    Privado

    Toda conexão é criptografada com X25519 e ChaCha20-Poly1305, com a senha entrando na derivação da chave. A identidade do servidor é conferida.

  4. 04

    Leve

    A interface só redesenha quando algo muda. O que você não usa não custa nada: nem CPU, nem GPU, nem banda.

  5. 05

    Portátil

    No Windows é um .exe só. No Linux roda em Wayland e X11, em qualquer distro com glibc 2.31 ou mais nova.

  6. 06

    Se atualiza sozinho

    Mostra o que muda, confere a assinatura da versão e troca o próprio executável. Com um clique, ou sozinho.

Conexão

Um convite, todos os caminhos

Você manda um link só. O app faz uma corrida entre os endereços dentro dele, e o primeiro que responde ganha.

telinha://192.168.0.10:7777,203.0.113.7:7777,[2804:14c::1]:7777,bore.pub:41234!kzdv…
  • Rede local—
  • Porta do roteador—
  • IPv6—
  • Link público—

O tempo está em câmera lenta pra dar pra ver. Seja qual for o caminho, a conversa é criptografada de ponta a ponta e o convite leva a impressão digital da sala: um túnel no meio só vê bytes embaralhados. Todos os jeitos de conectar →

Recebeu um link?

Clique nele. A página abre o telinha direto na sala ou mostra como baixar no seu sistema. O convite fica só no seu navegador.

Recebeu um telinha://?

Copie e abra o telinha: ele percebe o convite copiado. Ou cole no campo Entrar numa sala.

Tá no celular?

Não tem app pra celular. Se a sala tiver a página de assistir no navegador, peça o link dela pra quem te chamou.

Controle remoto

Troque de canal

Estes são os temas do próprio app, lidos direto do código dele. Aperte um, ou uma tecla de 0 a 6, e o site inteiro sintoniza, docs inclusive. O canal 00 é só do site: as cores dos olhos do husky.

Convidados

Bots

Música na call, e os seus, em Python.

Um bot é mais um na sala

Ele lê e manda mensagens, responde comandos, reage, toca som na call e mexe com arquivos. Aparece com a etiqueta BOT e nunca vira admin; o dono da sala pode recusar bots ou exigir um token.

  • Um bot de música pronto: links do YouTube, SoundCloud e Spotify, busca por nome, uma pasta, rádios. !play, !search, !queue, !skip.
  • Cartões com botões: capa, campos, uma barra de progresso que anda sozinha e botões que qualquer um clica; o bot muda o cartão no lugar.
  • Um SDK em Python: comandos com argumentos tipados, eventos, mensagens privadas, música e arquivos.
  • Roda numa VPS, num Raspberry Pi ou no seu PC. Não custa nada pra quem transmite.
dado.py
import random
from telinha import Bot

bot = Bot("telinha://minha.sala:7777#senha", "Dado")

@bot.command("dado", help="rola um dado")
async def dado(ctx, lados: int = 6):
    await ctx.reply(f"deu {random.randint(1, lados)}")

bot.run()
O bot de música numa sala: o pedido !play e o cartão TOCANDO AGORA com a barra de progresso e os botões
No app: o cartão do bot, com a barra que anda sozinha e os botões, e o anel verde de quem está tocando.
Arquivo

Capturas de tela

O app de verdade, desta vez nada simulado.

A sala. Quem está ao vivo em cima, a transmissão no meio, membros e chat do lado.
Entrar ao vivo. O que mostrar, qual som, o tipo de conteúdo, resolução, fps e qualidade.
Tela inicial. Entre numa sala ou crie uma, direto num canal se quiser.
Estatísticas do servidor. O que o admin vê: CPU, memória, banda e o gargalo provável.
Atalhos globais. Qualquer tecla ou botão do mouse, valendo com um jogo em foco.
Desempenho. Qualidade automática, cursor fora do vídeo e um teste do seu PC.
Sintonize

Instalar

Escolha o seu sistema.

Sem sudo: baixa a última versão, confere o SHA256, põe o telinha em ~/.local/bin, adiciona ao menu de apps e faz os convites telinha:// abrirem no app. Rode de novo pra atualizar.

$ curl -fsSL https://raw.githubusercontent.com/RshaCuDeVidro/telinha/main/install.sh | sh
  1. Crie uma sala. Abra o telinha e clique em Criar sala. A senha é opcional.
  2. Chame a galera. Abrir porta no roteador pra conexão direta, ou Criar link público se a operadora usa CGNAT. Depois, Copiar convite.
  3. Entre. Quem recebeu cola o convite telinha://… na tela inicial. Se já estiver copiado, ele se cola sozinho.
  4. Transmita. Compartilhar tela, escolha o que mostrar, o som e a qualidade, e Ao vivo. Dá pra mudar tudo rolando.
  5. Assista e fale. Clique em quem está ao vivo. F11 tela cheia, Ctrl+M microfone, Ctrl+D surdo. Guia completo →

Fim da transmissão

Baixe, crie uma sala e mande o convite. Se travar em alguma parte, a wiki tem o caminho.