Codificar áudio com cURL e ferramentas de código aberto
Use o cURL para baixar o áudio e o FFmpeg para codificá-lo. Este passo a passo gera um arquivo M4A local a partir de uma amostra MP3 real e depois transforma esse fluxo de trabalho em um script Bash para saída em AAC, M4A, MP3 ou Opus. Fazer upload do resultado exige um destino com o próprio contrato de API e está fora do escopo deste exemplo.
Configure seu ambiente
Use Linux com Bash, cURL, FFmpeg e ffprobe no seu PATH. O build do FFmpeg precisa dos codificadores aac,
libmp3lame e libopus. Instale pacotes mantidos para a sua distribuição;
a página de download do FFmpeg traz links para os provedores de pacotes.
Os exemplos foram testados com Bash 5.3.15, cURL 8.22.0 e FFmpeg/ffprobe 9.0.1, com uma nova
execução separada de compatibilidade no FFmpeg/ffprobe 6.1.1. Essas são as versões testadas, não uma
recomendação para instalar uma versão de patch antiga.
Verifique as ferramentas e os codificadores instalados:
bash --version &&
curl --version &&
ffmpeg -version &&
ffprobe -version &&
ffmpeg -hide_banner -encoders
Comece com uma gravação completa e sem criptografia, mono ou estéreo, a 44,1 ou 48 kHz. Os exemplos cobrem entradas MP3 e WAV PCM, incluindo amostras inteiras e de ponto flutuante. Eles não definem um mapeamento de canais surround nem forçam uma taxa de amostragem; o FFmpeg pode reamostrar quando o codec de saída exigir. Use áudio que você tenha permissão para processar.
Codificação de áudio básica com FFmpeg e cURL
Cole isto no Bash a partir de um diretório onde audio-example ainda não exista. O bloco baixa
viper.mp3 do exemplo Web Audio da MDN
e, em seguida, codifica o primeiro fluxo de áudio dele como AAC em um contêiner M4A. A amostra fixada
tem cerca de 41 segundos de áudio estéreo a 44,1 kHz.
(
set -euo pipefail
mkdir -- audio-example || exit 1
trap 'status=$?; if [ "$status" -ne 0 ]; then
rm -f -- audio-example/input.mp3 audio-example/output.m4a audio-example/ffmpeg-errors.log
rmdir -- audio-example 2>/dev/null || true
fi' EXIT
curl -fsSL --retry 3 --connect-timeout 10 --max-time 60 \
--proto '=https' --proto-redir '=https' \
-o audio-example/input.mp3 \
https://raw.githubusercontent.com/mdn/webaudio-examples/5af6b7a1545ad572c2dd2ce0443eeb26ab7c3e61/audio-analyser/viper.mp3 || exit 1
if ! ffmpeg -nostdin -v error -n -xerror -i audio-example/input.mp3 -map 0:a:0 \
-c:a aac -b:a 192k -f ipod audio-example/output.m4a \
2>audio-example/ffmpeg-errors.log || [ -s audio-example/ffmpeg-errors.log ]; then
cat -- audio-example/ffmpeg-errors.log >&2
exit 1
fi
rm -f -- audio-example/ffmpeg-errors.log
)
Em caso de sucesso, audio-example contém o MP3 baixado e output.m4a. Uma falha no download ou
na codificação remove os arquivos desta tentativa. Se o diretório já existir, o bloco para antes de
gravar qualquer coisa. Os parênteses mantêm as opções do shell dentro de um subshell, então colar o
bloco não altera o shell em que você está trabalhando. Execute este exemplo sequencialmente em um
diretório que você controla.
A opção -map 0:a:0 seleciona o primeiro fluxo
de áudio, enquanto -n se recusa a substituir uma saída existente e -nostdin desativa a entrada interativa.
O bitrate é uma meta para o codificador, não uma garantia de tamanho de arquivo ou de qualidade
perceptual. Recodificar um MP3 não recupera detalhes já perdidos na compressão original.
Crie um script de codificação de áudio
Salve o conteúdo a seguir como encode_audio.sh no diretório atual. O argumento de formato escolhe
tanto um codificador quanto um contêiner; mudar apenas a extensão do nome do arquivo não converte o
áudio. A documentação de formatos do FFmpeg descreve esses muxers.
| Argumento | Codec de áudio | Contêiner | Arquivo de saída |
|---|---|---|---|
aac | AAC | ADTS | output.aac |
m4a | AAC | Áudio MPEG-4 | output.m4a |
mp3 | MP3 via libmp3lame | MP3 | output.mp3 |
opus | Opus via libopus | Ogg | output.opus |
#!/bin/bash
set -euo pipefail
if [ "$#" -ne 3 ]; then
echo "Usage: $0 <input_url> <output_format> <output_bitrate>" >&2
exit 1
fi
INPUT_URL=$1
OUTPUT_FORMAT=$2
BITRATE=$3
case "$OUTPUT_FORMAT" in
aac) CODEC=aac; CONTAINER=adts ;;
m4a) CODEC=aac; CONTAINER=ipod ;;
mp3) CODEC=libmp3lame; CONTAINER=mp3 ;;
opus) CODEC=libopus; CONTAINER=ogg ;;
*) echo "Unsupported output format: $OUTPUT_FORMAT" >&2; exit 1 ;;
esac
if [[ ! "$BITRATE" =~ ^[1-9][0-9]*k$ ]]; then
echo "Bitrate must be a positive integer followed by k, such as 192k" >&2
exit 1
fi
WORK_DIR=$(mktemp -d ./audio-encode.XXXXXX)
INPUT_FILE="$WORK_DIR/input.audio"
OUTPUT_FILE="$WORK_DIR/output.$OUTPUT_FORMAT"
ERROR_LOG="$WORK_DIR/ffmpeg-errors.log"
# Delete the download; retain the output only after successful encoding.
trap 'status=$?; rm -f -- "$INPUT_FILE" "$ERROR_LOG";
if [ "$status" -ne 0 ]; then rm -f -- "$OUTPUT_FILE"; fi
rmdir -- "$WORK_DIR" 2>/dev/null || true' EXIT
echo "Downloading input file…"
if ! curl -fsSL --retry 3 --connect-timeout 10 --max-time 60 \
--proto '=http,https' --proto-redir '=http,https' \
-o "$INPUT_FILE" -- "$INPUT_URL"; then
echo "Error: Failed to download input file" >&2
exit 1
fi
echo "Encoding to ${OUTPUT_FORMAT} format…"
if ! ffmpeg -nostdin -v error -n -xerror -i "$INPUT_FILE" -map 0:a:0 -vn \
-c:a "$CODEC" -b:a "$BITRATE" -f "$CONTAINER" "$OUTPUT_FILE" \
2>"$ERROR_LOG" || [ -s "$ERROR_LOG" ]; then
cat -- "$ERROR_LOG" >&2
echo "Error: Failed to encode audio" >&2
exit 1
fi
echo "Successfully encoded to: ${OUTPUT_FILE}"
Execute o script salvo com o Bash; ele não precisa de permissão de execução:
bash ./encode_audio.sh \
https://raw.githubusercontent.com/mdn/webaudio-examples/5af6b7a1545ad572c2dd2ce0443eeb26ab7c3e61/audio-analyser/viper.mp3 \
opus 128k
Em caso de sucesso, o script imprime um caminho como ./audio-encode.A1b2C3/output.opus. Cada execução cria um novo
diretório de trabalho, então executar de novo a mesma URL preserva os resultados anteriores. O script
remove o arquivo baixado e, em caso de falha no download ou na codificação, remove qualquer saída
parcial e o diretório de trabalho vazio. Erros de argumento ocorrem antes de ele criar arquivos.
Essa limpeza cobre falhas comuns de comandos; encerrar o processo à força ou desligar a máquina pode
deixar arquivos temporários.
128k significa uma meta de 128.000 bits por segundo. O script verifica a grafia do argumento;
ainda é o codificador selecionado que decide se esse bitrate é suportado. O download termina antes
da codificação, então o FFmpeg pode buscar posições dentro do arquivo de entrada.
Processamento de arquivos de áudio em lote
Salve isto como batch_encode.sh ao lado de encode_audio.sh. Execute-o a partir desse diretório. Cada linha
da lista de entrada tem uma URL, um formato e um bitrate separados por espaços. Linhas em branco são
ignoradas; aplique percent-encoding aos espaços dentro das URLs. Comentários e campos extras não são
suportados.
#!/bin/bash
set -euo pipefail
if [ "$#" -ne 1 ]; then
echo "Usage: $0 <input_file_list.txt>" >&2
echo "File list format: <input_url> <output_format> <bitrate>" >&2
exit 1
fi
INPUT_LIST=$1
failed=0
while IFS=' ' read -r url format bitrate extra || [[ -n "$url" ]]; do
[[ -z "$url" ]] && continue
echo "Processing: ${url}"
if [[ -n "$extra" || -z "$format" || -z "$bitrate" ]]; then
echo "Invalid list entry: $url" >&2
failed=1
elif bash ./encode_audio.sh "$url" "$format" "$bitrate"; then
echo "Success: ${url}"
else
echo "Failed: ${url}" >&2
failed=1
fi
done < "${INPUT_LIST}"
exit "$failed"
Por exemplo, salve estas duas linhas como audio-list.txt:
https://raw.githubusercontent.com/mdn/webaudio-examples/5af6b7a1545ad572c2dd2ce0443eeb26ab7c3e61/audio-analyser/viper.mp3 m4a 192k
https://raw.githubusercontent.com/mdn/webaudio-examples/5af6b7a1545ad572c2dd2ce0443eeb26ab7c3e61/audio-analyser/viper.mp3 mp3 128k
bash ./batch_encode.sh audio-list.txt
O lote é executado sequencialmente e mantém as saídas bem-sucedidas mesmo quando outra linha falha. Ele segue adiante após entradas inválidas, downloads com falha e codificações com falha e, ao final, encerra com status 1 se alguma tarefa tiver falhado. O status 0 significa que todas as linhas processadas foram bem-sucedidas; uma lista vazia não executa nenhum trabalho.
Considerações de segurança
Use URLs HTTPS de fontes em que você confia. O cURL
verifica certificados do servidor por padrão; não adicione -k para
contornar essa verificação. As
restrições --proto e --proto-redir do script permitem apenas
HTTP e HTTPS, inclusive em redirecionamentos. O HTTP continua disponível para um servidor de teste
local, mas não oferece criptografia de transporte.
Estes scripts são exemplos de conversão local. Eles não isolam o FFmpeg em sandbox, não impõem limites de tamanho de download nem tornam seguro que um servidor busque URLs arbitrárias fornecidas por usuários. Os timeouts do cURL limitam cada tentativa de transferência, e as novas tentativas podem fazer o download total demorar mais. Reserve espaço em disco suficiente para a entrada e a saída completas.
Tratamento de erros e validação
Confira a saída real do exemplo básico usando este bloco no mesmo diretório em que você o executou:
(
set -euo pipefail
VERIFY_LOG=$(mktemp ./audio-verify.XXXXXX)
trap 'rm -f -- "$VERIFY_LOG"' EXIT
ffprobe -v error -select_streams a:0 \
-show_entries stream=codec_name,sample_rate,channels:format=format_name,duration \
-of json audio-example/output.m4a || exit 1
if ! ffmpeg -nostdin -v error -xerror -i audio-example/output.m4a \
-map 0:a:0 -f null - 2>"$VERIFY_LOG" || [ -s "$VERIFY_LOG" ]; then
cat -- "$VERIFY_LOG" >&2
exit 1
fi
)
O esperado é que codec_name seja aac, com dois canais e duração próxima de 41 segundos. O ffprobe informa o
contêiner M4A como parte da família mov,mp4,m4a,3gp,3g2,mj2. O segundo comando decodifica a saída inteira
sem salvar outro arquivo. Para o resultado de um script, substitua nos dois comandos o caminho exato
impresso por aquela execução. O AAC ADTS bruto inclui atraso e preenchimento do codificador, e o
ffprobe pode estimar a duração dele a partir do bitrate. Compare o áudio decodificado com a linha do
tempo da origem em vez de tratar essa estimativa como uma duração exata.
-xerror pede que o FFmpeg pare ao encontrar erros.
O FFmpeg 6.1.1 pode relatar um erro tardio do decodificador e ainda assim retornar status 0. Por
isso, os blocos também capturam o stderr no nível de log error e rejeitam um log de erros não vazio,
imprimindo o diagnóstico antes da limpeza. Isso detecta erros relatados, mas não é uma prova de
integridade: uma gravação truncada em um ponto decodificável ainda pode ser codificada ou
decodificada com sucesso, e alguns pacotes danificados podem ser descartados silenciosamente.
Compare a duração com uma fonte sabidamente completa e ouça até o fim; use um checksum confiável do
publicador quando houver um disponível. A existência do arquivo, um cabeçalho legível e um status de
saída zero não comprovam que todo o áudio esperado chegou.
Um erro HTTP do cURL, como 404, interrompe o download antes da codificação. Um codificador indisponível, um arquivo sem fluxo de áudio ou um bitrate rejeitado pelo codificador interrompe a conversão. Mantenha o diagnóstico no stderr ao investigar uma falha. Se o lote encerrar com status 1, use as mensagens de cada linha para identificar as falhas; as saídas já concluídas continuam disponíveis.
Conclusão
Para um fluxo de trabalho de codificação hospedado, consulte a documentação do Robot 🤖 /audio/encode. O contrato de upload e autenticação dele é separado deste download local com cURL e desta conversão com FFmpeg.
