Difference between revisions of "Anri-chan/Source/mp4nerf.pl"

From SDA Knowledge Base

Jump to: navigation, search
(New page: <pre><nowiki> #!/usr/bin/perl use warnings; use strict 'subs'; package mp4; exit &mp4batchnerf(@ARGV) unless caller(); ################################################ # mp4nerf.pl # c v...)
 
m (fixed a bug parsing inside atoms with 64 bit lengths)
 
(One intermediate revision by the same user not shown)
Line 3: Line 3:
 
use warnings;
 
use warnings;
 
use strict 'subs';
 
use strict 'subs';
package mp4;
+
package mp4nerf;
  
exit &mp4batchnerf(@ARGV) unless caller();
+
$action = 'nerf';  # default when called from anri
 
+
{ no warnings 'once'; *anri::mp4nerf = *mp4nerf; }
################################################
+
exit &mp4batchnerf(@ARGV) unless defined caller();
# mp4nerf.pl
+
# c version by DJ Grenola on 17 July 2007
+
# perl version by SelbyMD on 20 September 2008
+
# license: none (public domain)
+
################################################
+
  
 
sub mp4batchnerf {
 
sub mp4batchnerf {
print "MP4 audio track disabler\n";
+
$verbose = 1;
print "beta 0.2, DJ Grenola & SelbyMD\n\n";
+
$action  = 'print';  # default when called stand-alone
print "This software comes with no warranty.\n\n";
+
&pout("MP4 audio track disabler\n");
 +
&pout("beta 0.2, DJ Grenola & SelbyMD\n\n");
 +
&pout("This software comes with no warranty.\n\n");
 
unless (grep {-e} @_) {
 
unless (grep {-e} @_) {
print "Usage: mp4nerf.pl    <MP4 file> [ <MP4 file> ... ] to print track information\n";
+
&perr("Usage: mp4nerf.pl    <MP4 file> [ <MP4 file> ... ] to print track information\n");
print "Usage: mp4nerf.pl -n <MP4 file> [ <MP4 file> ... ] to disable commentary\n";
+
&perr("Usage: mp4nerf.pl -n <MP4 file> [ <MP4 file> ... ] to disable commentary\n");
print "Usage: mp4nerf.pl -u <MP4 file> [ <MP4 file> ... ] to re-enable commentary\n";
+
&perr("Usage: mp4nerf.pl -u <MP4 file> [ <MP4 file> ... ] to re-enable commentary\n");
 
return 1;
 
return 1;
 
}
 
}
$action = 'print';
 
 
for $arg (@_) {
 
for $arg (@_) {
 
if    ($arg eq '-p') { $action = 'print'  }
 
if    ($arg eq '-p') { $action = 'print'  }
Line 33: Line 29:
 
}
 
}
 
if ($errors) {
 
if ($errors) {
printf "[-] %s file%s had errors during processing.\n",
+
&perr("[-] There were errors during processing. [${errors}/${files}]\n");
    ($files == 1) ? ('The', '') : ("${errors} of ${files}", 's');
+
 
} else {
 
} else {
printf "[+] %s file%s processed successfully.\n",
+
&pout("[+] All files processed successfully.\n");
    ($files == 1) ? ('The', ' was') : ("All ${files}", 's were');
+
 
}
 
}
 
return $errors;
 
return $errors;
Line 47: Line 41:
 
$tracks_to_skip = ($action eq 'nerf') ? 1 : 0;
 
$tracks_to_skip = ($action eq 'nerf') ? 1 : 0;
 
unless (open(FH, '+<:raw', $mp4file)) {
 
unless (open(FH, '+<:raw', $mp4file)) {
print qq([-] Failed to open file "${mp4file}".\n\n);
+
&perr(qq([-] Failed to open file "${mp4file}".\n\n));
 
return 1;
 
return 1;
 
}
 
}
print qq([+] Scanning file "${mp4file}".\n);
+
&pout(qq([+] Scanning file "${mp4file}".\n));
 
@traks = &getatomtree([qw(moov trak)], [0, -s $mp4file]);
 
@traks = &getatomtree([qw(moov trak)], [0, -s $mp4file]);
 
unless (@traks) {
 
unless (@traks) {
print qq([-] No tracks found. Maybe this isn't an mp4 file.\n\n);
+
&perr(qq([-] No tracks found. Maybe this isn't an mp4 file.\n\n));
 
close(FH);
 
close(FH);
 
return 1;
 
return 1;
Line 61: Line 55:
 
($offset, $id, $status, $type) = ($$tkhd[0], &id($tkhd), &status($tkhd), &type($trak));
 
($offset, $id, $status, $type) = ($$tkhd[0], &id($tkhd), &status($tkhd), &type($trak));
 
unless (defined($id) and defined($status)) {
 
unless (defined($id) and defined($status)) {
print qq([-] Error reading from file "${mp4file}".\n\n);
+
&perr(qq([-] Error reading from file "${mp4file}".\n\n));
 
close(FH);
 
close(FH);
 
return 1;
 
return 1;
 
}
 
}
printf "[+] Track %d (%7s,%8s) at 0x%08x, ", $id, $type, $status, $$trak[0] - 8;
+
printf("[+] Track %d (%7s,%8s) at 0x%08x, ", $id, $type, $status, $$trak[0] - 8) if $verbose;
 
if ($type ne 'audio') {
 
if ($type ne 'audio') {
print "ignored.\n";
+
&pout("ignored.\n");
 
} elsif (++$matches <= $tracks_to_skip) {
 
} elsif (++$matches <= $tracks_to_skip) {
print "skipped.\n";
+
&pout("skipped.\n");
 
} elsif (($action eq 'nerf') and ($status eq 'enabled')) {
 
} elsif (($action eq 'nerf') and ($status eq 'enabled')) {
print "attempting to disable ...\n";
+
&pout("attempting to disable ...\n");
 
if (nerf($offset)) {
 
if (nerf($offset)) {
print "[+] Nerfed successfully.\n";
+
&pout("[+] Nerfed successfully.\n");
 
} else {
 
} else {
print "[-] Failed to nerf this track.\n\n";
+
&perr("[-] Failed to nerf this track.\n\n");
 
close(FH);
 
close(FH);
 
return 1;
 
return 1;
 
}
 
}
 
} elsif (($action eq 'unnerf') and ($status eq 'disabled')) {
 
} elsif (($action eq 'unnerf') and ($status eq 'disabled')) {
print "attempting to enable ...\n";
+
&pout("attempting to enable ...\n");
 
if (unnerf($offset)) {
 
if (unnerf($offset)) {
print "[+] Unnerfed successfully.\n";
+
&pout("[+] Unnerfed successfully.\n");
 
} else {
 
} else {
print "[-] Failed to unnerf this track.\n\n";
+
&perr("[-] Failed to unnerf this track.\n\n");
 
close(FH);
 
close(FH);
 
return 1;
 
return 1;
 
}
 
}
 
} else {
 
} else {
print "no action taken.\n";
+
&pout("no action taken.\n");
 
}
 
}
 
}
 
}
print qq([+] File "$mp4file" processed successfully.\n\n);
+
&pout(qq([+] File "$mp4file" processed successfully.\n\n));
 
close(FH);
 
close(FH);
 
return 0;
 
return 0;
 
}
 
}
 +
 +
sub pout { print STDOUT @_ if $verbose }
 +
sub perr { print STDERR @_ }
  
 
sub myread  { sysseek(FH, $_[1], 0) and ( sysread(FH, $_[0], $_[2]) == $_[2]) }
 
sub myread  { sysseek(FH, $_[1], 0) and ( sysread(FH, $_[0], $_[2]) == $_[2]) }
Line 113: Line 110:
 
($atomname, $start, $end) = @_;
 
($atomname, $start, $end) = @_;
 
@atoms = ();
 
@atoms = ();
use bytes;   # needed for substr here
+
use bytes;
 
while ($start < $end) {
 
while ($start < $end) {
 
last unless &myread($temp, $start, 8);
 
last unless &myread($temp, $start, 8);
($size, $name) = (unpack('N', substr($temp, 0, 4)), substr($temp, 4, 4));
+
($size, $name) = unpack('NA4', $temp);
 
if ($size == 1) {    # might not have 'Q', so do this manually
 
if ($size == 1) {    # might not have 'Q', so do this manually
 
last unless &myread($temp, $start + 8, 8);
 
last unless &myread($temp, $start + 8, 8);
 
$size = unpack('N', substr($temp, 0, 4)) * 2**32 + unpack('N', substr($temp, 4, 4));
 
$size = unpack('N', substr($temp, 0, 4)) * 2**32 + unpack('N', substr($temp, 4, 4));
 +
if ($size < 16) { $size = (-s $mp4file) - $start }
 +
if ($name eq $atomname) { push @atoms, [$start + 16, $start + $size] }
 +
} else {
 +
if ($size <  8) { $size = (-s $mp4file) - $start }
 +
if ($name eq $atomname) { push @atoms, [$start +  8, $start + $size] }
 
}
 
}
if ($size == 0)        { $size = (-s $mp4file) - $start; }
 
if ($name eq $atomname) { push @atoms, [$start + 8, $start + $size]; }
 
 
$start += $size;
 
$start += $size;
 
}
 
}

Latest revision as of 17:48, 6 December 2008

#!/usr/bin/perl
use warnings;
use strict 'subs';
package mp4nerf;

$action = 'nerf';  # default when called from anri
{ no warnings 'once'; *anri::mp4nerf = *mp4nerf; }
exit &mp4batchnerf(@ARGV) unless defined caller();

sub mp4batchnerf {
	$verbose = 1;
	$action  = 'print';  # default when called stand-alone
	&pout("MP4 audio track disabler\n");
	&pout("beta 0.2, DJ Grenola & SelbyMD\n\n");
	&pout("This software comes with no warranty.\n\n");
	unless (grep {-e} @_) {
		&perr("Usage: mp4nerf.pl    <MP4 file> [ <MP4 file> ... ] to print track information\n");
		&perr("Usage: mp4nerf.pl -n <MP4 file> [ <MP4 file> ... ] to disable commentary\n");
		&perr("Usage: mp4nerf.pl -u <MP4 file> [ <MP4 file> ... ] to re-enable commentary\n");
		return 1;
	}
	for $arg (@_) {
		if    ($arg eq '-p') { $action = 'print'  }
		elsif ($arg eq '-u') { $action = 'unnerf' }
		elsif ($arg eq '-n') { $action = 'nerf'   }
		elsif ($arg eq '-x') { $action = 'nerf'   }
		else                 { $files++; $errors += &mp4nerf($arg) }
	}
	if ($errors) {
		&perr("[-] There were errors during processing. [${errors}/${files}]\n");
	} else {
		&pout("[+] All files processed successfully.\n");
	}
	return $errors;
}

sub mp4nerf {
	$mp4file        = shift;
	$matches        = 0;
	$tracks_to_skip = ($action eq 'nerf') ? 1 : 0;
	unless (open(FH, '+<:raw', $mp4file)) {
		&perr(qq([-] Failed to open file "${mp4file}".\n\n));
		return 1;
	}
	&pout(qq([+] Scanning file "${mp4file}".\n));
	@traks = &getatomtree([qw(moov trak)], [0, -s $mp4file]);
	unless (@traks) {
		&perr(qq([-] No tracks found. Maybe this isn't an mp4 file.\n\n));
		close(FH);
		return 1;
	}
	for $trak (@traks) {
		($tkhd) = &getatoms('tkhd', @$trak);
		($offset, $id, $status, $type) = ($$tkhd[0], &id($tkhd), &status($tkhd), &type($trak));
		unless (defined($id) and defined($status)) {
			&perr(qq([-] Error reading from file "${mp4file}".\n\n));
			close(FH);
			return 1;
		}
		printf("[+] Track %d (%7s,%8s) at 0x%08x, ", $id, $type, $status, $$trak[0] - 8) if $verbose;
		if ($type ne 'audio') {
			&pout("ignored.\n");
		} elsif (++$matches <= $tracks_to_skip) {
			&pout("skipped.\n");
		} elsif (($action eq 'nerf') and ($status eq 'enabled')) {
			&pout("attempting to disable ...\n");
			if (nerf($offset)) {
				&pout("[+] Nerfed successfully.\n");
			} else {
				&perr("[-] Failed to nerf this track.\n\n");
				close(FH);
				return 1;
			}
		} elsif (($action eq 'unnerf') and ($status eq 'disabled')) {
			&pout("attempting to enable ...\n");
			if (unnerf($offset)) {
				&pout("[+] Unnerfed successfully.\n");
			} else {
				&perr("[-] Failed to unnerf this track.\n\n");
				close(FH);
				return 1;
			}
		} else {
			&pout("no action taken.\n");
		}
	}
	&pout(qq([+] File "$mp4file" processed successfully.\n\n));
	close(FH);
	return 0;
}

sub pout { print STDOUT @_ if $verbose }
sub perr { print STDERR @_ }

sub myread  { sysseek(FH, $_[1], 0) and ( sysread(FH, $_[0], $_[2]) == $_[2]) }
sub mywrite { sysseek(FH, $_[1], 0) and (syswrite(FH, $_[0], $_[2]) == $_[2]) }

sub nerf   { &myread($temp, $_[0] + 3, 1) and &mywrite(pack('C', unpack('C', $temp) & 0xFE), $_[0] + 3, 1) }
sub unnerf { &myread($temp, $_[0] + 3, 1) and &mywrite(pack('C', unpack('C', $temp) | 0x01), $_[0] + 3, 1) }

sub isvideo { &getatomtree([qw(mdia minf vmhd)], $_[0]) }
sub isaudio { &getatomtree([qw(mdia minf smhd)], $_[0]) }

sub type   { &isaudio ? 'audio' : &isvideo ? 'video' : 'unknown' }
sub id     { &myread($temp, $_[0][0] + 12, 4) ?   unpack('N', $temp) : 0 }
sub status { &myread($temp, $_[0][0] +  3, 1) ? ((unpack('C', $temp) % 2) ? 'enabled' : 'disabled') : undef }

sub getatoms {
	($atomname, $start, $end) = @_;
	@atoms = ();
	use bytes;
	while ($start < $end) {
		last unless &myread($temp, $start, 8);
		($size, $name) = unpack('NA4', $temp);
		if ($size == 1) {    # might not have 'Q', so do this manually
			last unless &myread($temp, $start + 8, 8);
			$size = unpack('N', substr($temp, 0, 4)) * 2**32 + unpack('N', substr($temp, 4, 4));
			if ($size < 16) { $size = (-s $mp4file) - $start }
			if ($name eq $atomname) { push @atoms, [$start + 16, $start + $size] }
		} else {
			if ($size <  8) { $size = (-s $mp4file) - $start }
			if ($name eq $atomname) { push @atoms, [$start +  8, $start + $size] }
		}
		$start += $size;
	}
	@atoms;
}

sub getatomtree {
	@tree = ($_[1]);
	for my $atomname (@{$_[0]}) {
		last unless @tree;
		@tree = map { &getatoms($atomname, @$_) } @tree;
	}
	@tree;
}

1;
Personal tools