2010-09-06 56 views
5

当我使用FLV::Info模块从多个FLV文件提取元数据或合并多个FLV文件时,我经常收到“Tag size is too small”错误,然后模块将拒绝工作。有人在三年前发布了一个错误报告here,但似乎没有修复。我可以在Perl中更改加载模块中的代码行吗?

好了,最近我发现,如果我只是注释掉的代码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文件的一个简单的方法?

任何意见,建议或意见? 感谢像往常一样:)

UPDATE

非常感谢@Shwern。您的回答非常令人满意:)还要感谢@DVK提供的建议,以及“猴子补丁”一词和@brian的书籍推荐。

这是我对FLV文件示例的测试反馈,如果我使用原始模块而不做任何事情,会导致“标记大小太小”错误。

的 “EVAL回来” 的方式解决了这个问题

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.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回来”和“控死没死”的方法给我以下内容:

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', 
); 
+1

我在_Mastering Perl_中深入介绍了这些东西。 – 2010-09-06 18:36:40

回答

18

简单吗?不,但你可以做一些疯狂的事情。这是一些不好的想法。

更明显的一点是将.pm文件的黑客副本放入您的项目中,以便在系统版本之前看到它。

另一个是类似的,但要剪切&将整个例程粘贴到代码中并在加载原始代码后注入它。

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; 

或者你也可以走这子程序的操作码树和改变的条件下,这是我离开作为练习用的人在他们的手更多的时间。

他们都是坏主意。您最好只更改代码并再次联系作者,让他们知道有新信息。

+0

这是一个很好的答案,有一个很好的免责声明:) – Konerak 2010-09-06 06:47:51

+0

@Schwern,谢谢你与我分享那些“坏主意”!非常有帮助:) – Mike 2010-09-06 07:19:43

+0

答案应该加入下一个关于Perl的“动态”属性意味着什么的讨论。 – Dummy00001 2010-09-06 10:33:29

3

OK,Schwern拥有的回答是很透彻,但这里有一个方法,将少“坏” ......

去与他的第二个方法(切&整个套路粘贴到你的代码,加载后它注入原始)...但是...条件它的具体版本FLV::Info(或FLV::Tag)。

这样,你仍然有你的猴子补丁(这在技术上被认为是猴子补丁?),但是你删除了这种方法被认为是“坏”的最大原因之一 - 即任何升级到模块可能与您的自定义修补程序子程序冲突。如果你用版本检查来防范覆盖,你可以消除这种担忧。

+1

我同意.123456 – Schwern 2010-09-06 19:39:30

相关问题