当我使用FLV::Info
模块从多个FLV文件中提取元数据或合并多个FLV文件时,我经常收到“标记大小太小”错误,然后模块将拒绝工作。有人在三年前发布了一个错误报告here,但似乎没有解决办法。
好吧,最近我发现我是否只是在Tag.pm中注释掉以下代码行,这是FLV::Info
的依赖模块之一,如下所示:
=pod
if ($datasize < 11)
{
die "Tag size is too small ($datasize) at byte " . $file->get_pos(-10);
}
=cut
然后 FLV::Info
将按预期方式开展工作。
我不确定这是一个非常愚蠢的问题,但我感到很好奇:
是否有一种简单的方法可以在不修改原始.pm文件的情况下更改已加载模块中的几行代码?
有任何想法,建议或意见吗? 总是如此谢谢:)
更新
非常感谢@Shwern。你的答案非常令人满意:)还要感谢@DVK的建议以及“猴子补丁”一词和@brian的书籍推荐。
以下是我对样本FLV文件测试的反馈,如果我使用原始模块而没有对其进行任何操作,则会导致“标记大小太小”错误。
“eval it back”方法解决了问题
use FLV::Info;
use Data::Dump::Streamer;
my $original = FLV::Tag->can("parse");
my $code = Dump($original)->Out;
#$code =~ s{\Qif ($datasize < 11)\E}{if (0)}; #This somehow won't work
$code =~ s{die "Tag}{warn "Tag}; #Let it warn but not die
no warnings 'redefine';
*FLV::Tag::parse = eval $code;
my $reader = FLV::Info->new();
$reader->parse('sample.flv');
my %info = $reader->get_info();
print "$info{video_count} video frames\n";
print $reader->report();
“过度死亡不死”方法也有效
BEGIN {
*CORE::GLOBAL::die = sub { return CORE::die(@_) };
}
use FLV::Info;
{
local *CORE::GLOBAL::die = sub {
return if $_[0] =~ /^Tag size is too small/;
return CORE::die(@_);
};
my $reader = FLV::Info->new();
$reader->parse('sample.flv');
my %info = $reader->get_info();
print "$info{video_count} video frames\n";
print $reader->report();
}
然而,“重新定义”方法并不像我预期的那样有效。
我复制并粘贴了原始的FLV :: Tag :: parse子例程,并按照我修改原始Tag.pm文件的方式注释掉了代码行:
use FLV::Info;
no warnings 'redefine';
*FLV::Tag::parse = sub {
...
...
=pod
if ($datasize < 11)
{
die "Tag size is too small ($datasize) at byte " . $file->get_pos(-10);
}
=cut
...
...
};
my $reader = FLV::Info->new();
$reader->parse('sample.flv');
my %info = $reader->get_info();
print "$info{video_count} video frames\n";
print $reader->report();
但是我收到了这个错误:
Unknown tag type 18 at byte 13 (0xd)
好吧,即使复制和粘贴完全相同的解析子程序而没有在重新定义中进行任何修改,我也会收到“未知标记类型”错误,而不是“标记尺寸太小”。
这很奇怪!
作为参考,“eval it back”和“override die to not die”方法将给我以下内容:
1992 video frames
File name sample.flv
File size 5767831 bytes
Duration about 79.6 seconds
Video 1992 frames
codec AVC
type interframe/keyframe
Audio 1712 packets
format AAC
rate 44100 Hz
size 16 bit
type stereo
Meta 1 event
audiocodecid 10
audiosamplerate 22050
audiosamplesize 16
audiosize 342817
creationdate unknown
datasize 805
duration 79.6
filesize 5767869
framerate 25
height 300
keyframes {
>>> 'filepositions' => [
>>> '780',
>>> '865',
>>> '1324122',
>>> '2348913',
>>> '2978630',
>>> '3479001',
>>> '3973756',
>>> '4476281',
>>> '4997226',
>>> '5391890'
>>> ],
>>> 'times' => [
>>> '0',
>>> '0',
>>> '9.6',
>>> '19.2',
>>> '28.8',
>>> '38.4',
>>> '46.32',
>>> '55.92',
>>> '64.88',
>>> '73.88'
>>> ]
>>> }
lastkeyframetimestamp 73.88
lasttimestamp 79.6
metadatacreator Manitu Group FLV MetaData Injector 2
metadatadate 1281964633858
stereo 1
videocodecid 7
videosize 5424234
width 400
最终更新
我已经弄清楚为什么“重新定义”方法失败了,打开严格和警告的实用工具。感谢@Schwern的提醒:)
首先添加以下代码行(从FLV :: Util模块复制),然后重新定义FLV :: Tag :: parse子例程。
Readonly::Hash our %TAG_CLASSES => (
8 => 'FLV::AudioTag',
9 => 'FLV::VideoTag',
18 => 'FLV::MetaTag',
);
答案 0 :(得分:18)
简单?不,但你可以做一些疯狂的事情。这是一些不好的想法。
更明显的一个是将.pm文件的被黑客拷贝放入你的项目中,这是在系统版本之前看到的。
另一个是类似的,但要削减&amp;将整个例程粘贴到代码中,并在加载原始代码后将其注入。
use FLV::Tag;
no warnings 'redefine';
*FLV::Tag::parse = sub {
...copy of FLV::Tag::parse with your edits...
};
您可以覆盖die
,以便在看到该消息时不会死亡。
BEGIN {
# In order to override die() later, you must override it at compile time.
*CORE::GLOBAL::die = sub { return CORE::die(@_) };
}
{
local *CORE::GLOBAL::die = sub {
return if $_[0] =~ /^Tag size too small/;
return CORE::die(@_);
}
...do your thing...
}
您可以将该子例程的内容转储回Perl,对代码执行字符串替换,然后将其重新评估。
use Data::Dump::Streamer;
my $original = FLV::Tag->can("parse");
my $code = Dump($original)->Out;
$code =~ s{\Qif ($datasize < 11)\E}{if( 0 )};
no warnings 'redefine';
*FLV::Tag::parse = eval $code;
或者你可以走这个子程序的操作码树并改变条件,我把它作为练习留给他们手上有更多时间的人。
他们都是坏主意。你最好只是更改代码并再次联系作者,让他们知道有新信息。
答案 1 :(得分:3)
好的,Schwern的答案非常彻底,但是这里的一种方法不那么“糟糕”......
使用他的第二种方法(将整个例程剪切并粘贴到您的代码中,并在加载原始代码后将其注入)...但是......在特定版本的FLV::Info
(或{{ 1}})。
这样,你仍然有你的猴子补丁(这在技术上被认为是猴子补丁?),但是你删除了这种方法被认为是“坏”的一个最大原因 - 即,任何升级到模块都可能与您的自定义修补子程序冲突。如果使用版本检查来保护覆盖,则可以消除这种担忧。