-hwaccel mediacodec이나 -c:v h264_mediacodec 같은 순정 FFmpeg 문법도
지원합니다. 자세한 내용은
순정 FFmpeg 문법 호환성 (MediaCodec)을
참고하세요.
DawnShell은 Debian의 일반 GPU를 패스스루하지 않습니다. Debian에서 컨테이너와
패킷을 정리하고, 명령별 bionic NDK worker의 MediaCodec이 H.264/AVC 및 HEVC
디코드와 인코드를 수행합니다.
앱의 파일 기반 하드웨어 AVC 디코드 자체 검사는 Android 하드웨어 디코더가 작동하는지만 확인합니다. 아래 FFmpeg 명령은 Debian 정적 client, 상속 공유 전송, 명령별 private NDK worker와 FFmpeg adapter를 별도로 확인합니다.
- 앱에서 다이렉트 부트에서 하드웨어 코덱 브리지 활성화를 켭니다.
- 하단의 적용을 누른 뒤 하드웨어 코덱 검사를 누릅니다.
- Debian 13 systemd + SSH 구성을 다시 실행합니다.
- Debian에서 다음을 확인합니다.
command -v dawnshell-ffmpeg dawnshell-hwdecode dawnshell-hwencode dawnshell-hwtranscode
sudo dawnshell-codec health --format jsonhealth JSON에는 다음 값이 모두 있어야 합니다.
worker_state=ready
transport=inherited_memfd_eventfd
public_listener=false
software_fallback=false
health는 새 worker를 시작해 상태를 읽고 회수합니다. 명령마다 PID가 달라지는
것이 정상이며 유지해야 할 daemon은 없습니다. 도구가 없다면 앱에서 Debian 구성을
다시 실행하세요.
다음 명령은 H.264 또는 HEVC 입력을 Android Surface 경로에서 디코드하고 AVC로
다시 인코드합니다. require를 사용했으므로 일반 FFmpeg로 조용히 폴백하지
않습니다.
sudo env DAWNSHELL_FFMPEG_BRIDGE=require \
dawnshell-ffmpeg -hide_banner -y \
-i input.mp4 -map 0:v:0 -an \
-c:v libx264 -b:v 4M output.mp4성공 로그에는 선택된 Android 코덱 이름과 surface_zero_copy, 입력·출력 프레임,
EOS 통계가 표시됩니다. OMX.google.* 또는 c2.android.* 같은 소프트웨어
코덱은 하드웨어 성공으로 인정하지 않습니다.
H.264/HEVC를 packed I420 파일로 디코드합니다.
sudo dawnshell-hwdecode input.mp4 output.i420출력 크기는 width × height × 3 / 2 × frame_count입니다. .mp4 같은 컨테이너를
출력으로 지정하면 디코드 뒤 /usr/bin/ffmpeg의 libx264로 다시 인코드하므로,
순수 디코드 검사에는 .i420 또는 .yuv를 사용하세요.
입력 영상은 일반 FFmpeg가 I420로 변환하고 Android 하드웨어 인코더가 AVC 또는 HEVC로 인코드합니다.
sudo dawnshell-hwencode input.mp4 output.mp4 4000000 avc
sudo dawnshell-hwencode input.mp4 output.hevc 4000000 hevc
# 첫 번째 입력 오디오를 재인코딩하지 않고 유지합니다.
sudo dawnshell-hwencode input.mp4 output.mp4 4000000 avc copy인자는 INPUT OUTPUT [BITRATE] [avc|hevc] [none|copy] 순서입니다. bitrate 범위는
1000~100000000 bit/s입니다. 일반 FFmpeg 문법도 지원합니다.
sudo ffmpeg -y -i input.mp4 -c:a copy \
-c:v h264_mediacodec output.mp4-c:a copy는 입력의 첫 번째 오디오 stream을 재인코딩하지 않고 하드웨어 인코딩된
영상과 mux합니다. 입력에 오디오가 없어도 optional mapping으로 정상 완료됩니다.
wrapper는 FFmpeg 배너, stream mapping과 실시간 frame/fps/time/speed 출력을
숨기지 않으며, 같은 stderr를 실패 진단 로그에도 보존합니다.
H.264/HEVC 입력을 Surface로 하드웨어 디코드하고, CPU로 전체 YUV 프레임을 돌려보내지 않은 채 AVC 하드웨어 인코더에 연결합니다.
sudo dawnshell-hwtranscode input.mp4 output.mp4 4000000현재 출력 코덱은 AVC입니다. 오디오 stream은 포함하지 않습니다.
dawnshell-live-encode는 입력을 FFmpeg로 실시간 캡처·디코드하고 I420 frame을
Android MediaCodec AVC 인코더로 보낸 다음, 결과 Annex-B stream을 파일이나
HLS로 즉시 mux합니다. 전체 입력과 출력을 임시 파일에 모으지 않습니다.
Debian 구성을 다시 실행한 뒤 설치 여부와 실제 실행 계획을 확인합니다.
command -v dawnshell-live-encode
dawnshell-live-encode --print-plan \
--input /dev/video0 --input-format v4l2 --input-pixel-format mjpeg \
--size 1280x720 --fps 30 --bitrate 4000000 \
--output recording.mp4USB UVC 웹캠을 실시간 AVC MP4로 저장합니다.
sudo dawnshell-live-encode \
--input /dev/video0 --input-format v4l2 --input-pixel-format mjpeg \
--size 1280x720 --fps 30 --bitrate 4000000 \
--output recording.mp4웹캠을 HLS로 송출합니다. 웹 서버가 /var/www/html/live를 제공한다고
가정합니다.
sudo install -d -m 0755 /var/www/html/live
sudo dawnshell-live-encode \
--input /dev/video0 --input-format v4l2 --input-pixel-format mjpeg \
--size 1280x720 --fps 30 --bitrate 4000000 \
--output-mode hls --hls-time 2 --hls-list-size 6 \
--hls-delete-segments --output /var/www/html/live/index.m3u8HLS와 fragmented MP4 녹화를 동시에 생성할 수도 있습니다.
sudo dawnshell-live-encode \
--input /dev/video0 --input-format v4l2 --input-pixel-format mjpeg \
--size 1920x1080 --fps 30 --bitrate 8000000 \
--output-mode hls --output /var/www/html/live/index.m3u8 \
--record /srv/video/webcam.mp4HLS·RTSP·일반 파일 입력을 하드웨어 AVC로 다시 인코드할 때는 입력 형식을
auto로 둡니다.
sudo dawnshell-live-encode \
--input 'https://example.invalid/live/index.m3u8' \
--input-format auto --size 1280x720 --fps 30 --bitrate 4000000 \
--output-mode hls --output /var/www/html/restream/index.m3u8이 실시간 경로에서 입력 디코드·색상 변환·크기 변환은 Debian FFmpeg가
수행하고 AVC 인코드만 Android 하드웨어가 수행합니다. 현재 오디오는
포함하지 않습니다. 인코더의 keyframe 간격은 2초이므로 HLS segment 목표도
2초가 가장 예측 가능합니다. Ctrl-C를 누르면 파이프라인을 종료하고 MP4/HLS
출력을 정상 마무리합니다.
USB 웹캠은 호스트 커널의 UVC/V4L2 지원, Debian에 노출된 /dev/videoX, devices
cgroup 허용, SELinux 접근 허용이 모두 필요합니다. 먼저 다음으로 지원 형식을
확인하세요.
ls -l /dev/video* 2>/dev/null
v4l2-ctl --list-formats-ext -d /dev/video0다음 명령은 미디어를 실행하지 않고 dawnshell-ffmpeg가 선택할 경로만 출력합니다.
/usr/local/libexec/dawnshell-codec-ffmpeg.pl plan-ffmpeg \
-hide_banner -y -i input.mp4 -map 0:v:0 -an \
-c:v libx264 -b:v 4M output.mp4이 예제의 결과는 다음과 같습니다.
action=transcode input=input.mp4 output=output.mp4 codec=avc bitrate=4000000
실제 실행 중 래퍼가 호출하는 셸 명령과 변수 값을 모두 보려면 선택된 직접 래퍼를
bash -x로 실행합니다.
sudo bash -x /usr/local/bin/dawnshell-hwtranscode \
input.mp4 output.mp4 4000000 \
2>&1 | tee /tmp/dawnshell-hwtranscode.trace.log디코드와 인코드도 같은 방식으로 추적할 수 있습니다.
sudo bash -x /usr/local/bin/dawnshell-hwdecode input.mp4 output.i420
sudo bash -x /usr/local/bin/dawnshell-hwencode input.mp4 output.mp4 4000000 avc아래는 H.264 MP4 입력을 AVC MP4로 변환하는
dawnshell-hwtranscode input.mp4 output.mp4 4000000의 핵심을 래퍼 없이 펼친
형태입니다. 일반 사용자라면 먼저 sudo -i로 root shell에 들어갑니다.
sudo -i
input=/absolute/path/input.mp4
output=/absolute/path/output.mp4
bit_rate=4000000
temporary="$(mktemp -d /run/dawnshell-raw-transcode.XXXXXX)"
trap 'rm -rf -- "$temporary"' EXIT HUP INT TERM
stream_info="$temporary/stream.txt"
input_packets="$temporary/input-packets.json"
annex_b="$temporary/input.h264"
raw_packets="$temporary/raw-packets.json"
framed_input="$temporary/framed-input.bin"
framed_output="$temporary/framed-output.bin"
encoded="$temporary/output.h264"
client_log="$temporary/client.log"
/usr/bin/ffprobe -v error -select_streams v:0 \
-show_entries stream=codec_name,width,height,avg_frame_rate \
-of default=noprint_wrappers=1 "$input" > "$stream_info"
width="$(sed -n 's/^width=//p' "$stream_info" | head -n 1)"
height="$(sed -n 's/^height=//p' "$stream_info" | head -n 1)"
frame_rate="$(sed -n 's/^avg_frame_rate=//p' "$stream_info" | head -n 1)"
integer_rate="$(printf '%s\n' "$frame_rate" | mawk -F/ \
'NF == 2 { printf "%d\\n", int(($1 / $2) + 0.5) }')"
/usr/bin/ffprobe -v error -select_streams v:0 -show_packets \
-show_entries packet=pts_time,dts_time -of json \
"$input" > "$input_packets"
/usr/bin/ffmpeg -hide_banner -loglevel error -y -i "$input" \
-map 0:v:0 -an -c:v copy -bsf:v h264_mp4toannexb \
-f h264 "$annex_b"
/usr/bin/ffprobe -v error -f h264 -show_packets \
-show_entries packet=pos,size -of json \
"$annex_b" > "$raw_packets"
/usr/local/libexec/dawnshell-codec-ffmpeg.pl pack \
"$input_packets" "$raw_packets" "$annex_b" "$frame_rate" "$framed_input"
/usr/local/bin/dawnshell-codec transcode \
avc avc "$width" "$height" "$integer_rate" "$bit_rate" \
< "$framed_input" > "$framed_output" 2> "$client_log"
cat "$client_log" >&2
frames="$(/usr/local/libexec/dawnshell-codec-ffmpeg.pl unpack-annexb \
"$framed_output" "$encoded" --require-keyframe)"
/usr/local/libexec/dawnshell-codec-ffmpeg.pl validate-stats \
"$client_log" "$frames"
/usr/bin/ffmpeg -hide_banner -loglevel error -y \
-r "$frame_rate" -f h264 -i "$encoded" \
-map 0:v:0 -an -c:v copy "$output"HEVC 입력은 h264_mp4toannexb를 hevc_mp4toannexb로, 두 -f h264를
-f hevc로, dawnshell-codec transcode avc avc를
dawnshell-codec transcode hevc avc로 바꿉니다.
여기서 /usr/local/bin/dawnshell-codec는 제거할 수 없는 정적 Android ELF
client입니다. 이 client가 memfd 하나와 eventfd 두 개를 만들고 fork한 다음,
그 descriptor만 상속한 /usr/local/libexec/dawnshell-codec-worker를 실행합니다.
bionic worker는 NDK AMediaCodec을 호출하고 상한이 있는 record를 반환한 뒤
부모와 함께 종료합니다. listening endpoint와 descriptor 전달은 없습니다. 일반
glibc FFmpeg만으로 MediaCodec을 직접 호출할 수 없습니다. 관리되는 native/chroot
실행 경로가 root 전용이므로 일반 Debian 계정에서는 sudo가 필요합니다.
FFmpeg와 이 client 사이에서 패킷을 프레이밍하는 도구가
/usr/local/libexec/dawnshell-codec-ffmpeg.pl입니다. 이 도구는 Debian이
Essential로 지정한 perl-base만 사용하므로, 최소 구성 Debian에서도 별도
인터프리터를 설치하지 않고 하드웨어 브리지가 동작합니다.
/usr/local/bin이 /usr/bin보다 앞에 있는 기본 Debian PATH에서 다음 symlink를
만들면, ffmpeg를 이름으로 실행하는 프로그램이 DawnShell 래퍼를 먼저 찾습니다.
sudo dawnshell-ffmpeg-integration enable
hash -r
command -v ffmpeg예상 경로는 /usr/local/bin/ffmpeg입니다. 래퍼 자체는 폴백할 때 항상
/usr/bin/ffmpeg를 직접 실행하므로 재귀 호출하지 않습니다. 프로그램이
/usr/bin/ffmpeg를 절대 경로로 실행하면 이 연동을 우회합니다.
이 연결은 Debian 구성 시 이미 기본으로 적용되므로 보통 다시 실행할 필요가 없습니다. 되돌리려면 다음을 실행합니다.
sudo dawnshell-ffmpeg-integration disable
hash -r| 값 | 동작 |
|---|---|
auto |
지원되는 명령은 하드웨어, 그 외에는 /usr/bin/ffmpeg |
require |
하드웨어 계획을 만들 수 없으면 오류로 종료 |
off |
항상 /usr/bin/ffmpeg |
sudo env DAWNSHELL_FFMPEG_BRIDGE=auto dawnshell-ffmpeg ...
sudo env DAWNSHELL_FFMPEG_BRIDGE=require dawnshell-ffmpeg ...
DAWNSHELL_FFMPEG_BRIDGE=off dawnshell-ffmpeg ...성능이나 호환성을 검사할 때는 반드시 require를 사용하세요. auto에서는 성공한
일반 FFmpeg 실행을 하드웨어 성공으로 오인할 수 있습니다.
- 입력 파일 하나와 출력 파일 하나
- 첫 번째 영상 stream
- H.264 또는 HEVC 입력
libx264/h264또는h264_mediacodecAVC 출력hevc_mediacodecHEVC 출력(ByteBuffer 인코드 경로).i420/.yuvraw 디코드 출력- 짝수 해상도 16~4096
- 1~240 fps
- bitrate 1000~100000000 bit/s
-hide_banner,-y,-n,-an,-nostdin,-loglevel,-v,-threads,-stats_period,-pix_fmt,-f,-r, 제한된-map- ByteBuffer 하드웨어 인코드에서 첫 번째 optional 오디오 stream의
-c:a copy
다음 요청은 일반 FFmpeg로 폴백하거나 require 모드에서 실패합니다.
-vf,-filter등 필터와 크기 변환-crf,-preset같은 x264/x265 전용 설정- 복수 입력 또는 복수 출력
- 임의의 영상 이외 stream mapping
-c:v copy- VP9, AV1 등 지원하지 않는 코덱
- 오디오 인코드·필터와 임의 mapping 옵션
Surface transcode는 영상 전용이므로 예제처럼 -an을 명시하세요. ByteBuffer
하드웨어 인코드는 첫 번째 optional 오디오 stream을 -c:a copy로 유지할 수
있습니다.
sudo ffmpeg -y -i input.mp4 -c:a copy \
-c:v h264_mediacodec -b:v 4M final.mp4지원 범위 밖의 오디오 조합은 영상 작업 뒤 일반 FFmpeg로 다시 mux합니다.
sudo env DAWNSHELL_FFMPEG_BRIDGE=require dawnshell-ffmpeg \
-y -i input.mp4 -map 0:v:0 -an -c:v h264_mediacodec -b:v 4M video-only.mp4
/usr/bin/ffmpeg -y -i video-only.mp4 -i input.mp4 \
-map 0:v:0 -map 1:a? -c:v copy -c:a copy final.mp4sudo dawnshell-codec health --format json
systemctl status dawnshell-codec-long-run.service --no-pager
journalctl -u dawnshell-codec-long-run.service -n 100 --no-pager앱에서는 하드웨어 영상 가속 → 하드웨어 코덱 보고서 보기를 엽니다.
- 파일 기반 검사는 성공하지만 FFmpeg가 실패함: 코덱 자체가 아니라 client/private-worker 경로를 확인합니다.
action=passthrough: 해당 명령은 자동 하드웨어 범위 밖입니다.require로 이유를 명확한 오류로 바꿀 수 있습니다.worker_state=ready가 없음: 앱에서 기능을 켜고 저장한 뒤 Debian 구성을 다시 실행하고 아래 파일과 Android runtime mount를 확인합니다.- BFU에서만 실패함: Android 미디어 서비스가 늦게 준비되는 ROM일 수 있습니다. 앱 로그의 locked-boot 재시도 결과를 확인합니다.
sudo test -x /usr/local/bin/dawnshell-codec
sudo test -x /usr/local/libexec/dawnshell-codec-worker
findmnt /system /apex /linkerconfig 2>/dev/null || true
sudo dawnshell-codec health --format jsonworker startup timed out은 대개 bionic dynamic linker 경로가 보이지 않거나 ready
handshake 전에 worker가 종료된 경우입니다. hardware codec ... unavailable은 해당
Android 플랫폼에서 허용된 하드웨어 component를 찾지 못한 경우입니다.
Connection refused는 폐기된 socket 기반 빌드의 오류이며 현재 구조에서는 나오면
안 됩니다.
시작 로그에 libandroidicu.so not found가 나오면 Debian에 Android의 읽기 전용 ICU
APEX를 private worker에 노출하지 않는 구형 정적 client가 남아 있는 것입니다.
DawnShell을 업데이트하고 앱을 연 뒤 Debian 13 systemd + SSH 구성을 다시
실행합니다. 현재 client는 상속된 linker 제어 환경 변수를 제거하고, 파일이 있는
경우 ABI에 맞는 /apex/com.android.i18n/lib[64]만 검색 경로에 추가합니다.
sudo dawnshell-codec health --format json의 worker_state=ready로 교체 성공을
확인할 수 있습니다.