2010-09-06 57 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

相關問題