Skip to content

Instantly share code, notes, and snippets.

@unstabler
Last active December 30, 2015 08:10
Show Gist options
  • Save unstabler/7b74d7d95605742ebdf4 to your computer and use it in GitHub Desktop.
Save unstabler/7b74d7d95605742ebdf4 to your computer and use it in GitHub Desktop.
X Window의 화면을 쉽게 동영상으로 캡쳐할 수 있도록 도와주는 스크립트
#!/usr/bin/env perl
#
# ffcaputre.pl
# - by unstabler / cheesekun (doping.cheese@gmail.com)
# http://unstabler.pl / twitter: @unstable_cheese
#
# ffmpeg를 이용하여 X 윈도우의 화면을 쉽게 동영상으로 캡쳐할 수 있도록 도와주는 스크립트입니다.
#
# TODO : 특정 영역 녹화 추가 ($ ffcapture.pl -x 0 -y 0 -w 1280 -h 720 out.avi)
use 5.010;
use utf8;
use strict;
use warnings;
BEGIN {
# 출력 버퍼를 비활성화합니다.
local $| = 1;
# STDERR로 메시지를 UTF-8로 출력합니다.
binmode "STDERR", ":encoding(utf8)";
}
my %apps;
{
my %options = (default_options(), parse_argv(), check_dependencies());
my %window = capture_window($options{target});
start_capture(\%window, \%options);
}
sub check_dependencies {
my %options;
# 실행 전 의존성을 체크합니다.
my @dependencies = qw/ffmpeg xwininfo/;
for (@dependencies) {
my $apppath = check_which($_);
die $_."가 설치되어 있지 않습니다. 패키지 관리자를 통해 설치해 주십시오." unless $apppath;
$apps{$_} = $apppath;
}
unless (check_x11grab()) {
die "ffmpeg에서 x11grab이 활성화 되어있지 않습니다. --enable-x11grab 옵션을 넣고 빌드해 주십시오.";
}
$options{display} = $ENV{DISPLAY};
die '$DISPLAY not defined. Please run this script on X11.' unless $options{display};
return %options;
}
sub check_which {
# which 명령어로 프로그램이 설치되어 있는지 체크합니다.
# which로 찾을 수 없거나 따로 빌드해둔 버전을 사용하고 싶은 경우 FORCE_USE_FFMPEG 등의 환경 변수를 지정해 주세요.
my $appname = shift;
my $user_defined = $ENV{"FORCE_USE_".uc($appname)};
if ($user_defined) {
return $user_defined if -e $user_defined;
warn (sprintf "경고: %s를 %s로 사용하도록 지정했지만 존재하지 않았습니다.", $appname, $user_defined);
}
my $which = `which $appname` || "";
chomp $which;
return if !$which || $which =~ /not found/;
return $which;
}
sub check_x11grab {
my $ffmpeg_buildconf = `$apps{ffmpeg} -buildconf 2>&1`;
return if $ffmpeg_buildconf !~ m/--enable-x11grab/;
return 1;
}
sub parse_argv {
# TODO: FIXME: 부실한 @ARGV 파싱
# 졸리니까 어쩔 수 없어.. 아 내일 데이터베이스.. 시험.. 아.. 9시까지..
# usage: ffcapture.pl [options] outfile
# $ ffcapture.pl --fullscreen "동영상.mp4"
#
# ffcapture.pl --help
# ffcapture.pl "치즈.avi"
my %parsed_options;
for (my $i = 0; $i <= $#ARGV; $i++) {
my $option = $ARGV[$i];
if ($option eq "--help") {
warn usage();
exit;
}
if ($option eq "--ffmpeg-options") {
# read more
my $value = $ARGV[++$i];
$parsed_options{ffmpeg} = $value;
} elsif ($option eq "--fullscreen") {
$parsed_options{target} = "fullscreen";
} elsif ($option eq "--disable-audio") {
$parsed_options{audio}{enable} = 0;
} elsif ($option eq "--overwrite") {
$parsed_options{overwrite} = 1;
} elsif ($i == $#ARGV) {
$parsed_options{outfile} = $option;
}
}
return %parsed_options;
}
sub usage {
my ($script_name) = $0 =~ m{([^/]+)$};
"usage: $script_name [options] [outfile]\n".
"X 윈도우의 동영상 캡쳐를 도와주는 스크립트입니다.".
$/ x2 .
"옵션 : \n".
"--ffmpeg-options '[ffmpeg options]'\n".
"\tffmpeg의 인코딩 옵션을 지정합니다.\n".
"\t(예시: --ffmpeg-options '-acodec libfaac -b:a 64k')\n".
$/ x2 .
"--fullscreen 전체 화면을 녹화합니다.\n".
"--disable-audio 오디오 녹음을 하지 않습니다.\n".
"--overwrite 파일이 이미 있어도 덮어씁니다.\n".
"--help 이 도움말을 표시합니다.\n\n".
"치즈군★ (doping.cheese\@gmail.com)\n".
"Seoul.pm 파이팅! :-)\n";
}
sub capture_window {
my $target = shift;
my $option;
my %window_info;
if ($target eq 'fullscreen') {
$option = '-root';
} elsif ($target eq 'window') {
warn "캡쳐하실 창을 마우스로 선택해 주세요.\n";
$option = '-frame';
} elsif ($target eq 'window_noframe') {
$option = '';
}
my $xwininfo = `$apps{xwininfo} $option`;
($window_info{geometry}) = $xwininfo =~ m/-geometry\s+(\d+x\d+)/;
($window_info{corners}{x}, $window_info{corners}{y}) = $xwininfo =~ m/Corners:\s+([+-]+?\d+)([+-]+?\d+)/;
return %window_info;
}
sub start_capture {
my $ffmpeg_options = to_ffmpeg_options(@_);
my %window_info = %{(shift)};
# TODO : ffmpeg에서 스크립트로 파이프 열고 SIGINT 캐치해서 좀 정상적으로 종료할 수 있도록 해야 합니다
warn "q 키를 눌러서 녹화를 종료할 수 있습니다.\n";
system("ffmpeg $ffmpeg_options");
}
sub to_ffmpeg_options {
my %window_info = %{(shift)};
my %options = %{(shift)};
my (@opt_input, @opt_encode);
if ($options{overwrite}) {
push @opt_input, '-y';
}
if ($options{audio}{enable}) {
push @opt_input,
sprintf "-f %s -i %s", $options{audio}{f}, $options{audio}{i};
} else {
push @opt_encode, "-an";
}
push @opt_input,
sprintf '-f x11grab -s "%s" -i "%s%s,%s"', $window_info{geometry}, $options{display},
$window_info{corners}{x}, $window_info{corners}{y};
push @opt_encode, ($options{ffmpeg}, "'$options{outfile}'");
return (join $", @opt_input)." ".(join $", @opt_encode);
}
###################
# Default Options #
###################################
# 기본 설정을 변경할 수 있습니다. #
# 설정값 변경은 조심조심! #
###################################
sub default_options {
return (
'audio' => {
'enable' => 1,
# ffmpeg 오디오 입력 설정입니다.
# ALSA 사용 시에는 다음 링크의 문서를 참고하시면 좋습니다.
# 다들 ALSA 쓰시는데 전 안써서 어떻게 도움을 못 드려요.. :-(
# https://trac.ffmpeg.org/wiki/Capture/ALSA
'f' => 'alsa',
'i' => 'hw:0',
# PulseAudio의 경우 Monitor of ... 으로 스테레오 믹스와 같이 오디오 루프백도 가능합니다.
# pactl list sources 명령으로 사용 가능한 장치를 확인해 보세요!
#
# 'f' => 'pulse',
# 'i' => 'alsa_output.pci-0000_00_1b.0.analog-stereo.monitor',
},
# ffmpeg의 인코딩 설정을 지정합니다.
# 기본값으로도 충분히 멋진 결과물이 나올거에요!
'ffmpeg' => '-vcodec libx264 -r 60 -preset ultrafast -crf 18 -acodec libmp3lame -b:a 160k',
'target' => 'window',
# 출력 파일 이름
'outfile' => 'out.mp4',
# 파일 덮어쓰기 여부
'overwrite' => 0,
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment