2010-09-06 3 views
5

Когда я использую модуль FLV::Info для извлечения метаданных из или слияния нескольких файлов FLV, я часто получаю ошибку «Размер тега слишком мал», а затем модуль просто откажется работать. Кто-то выпустил отчет об ошибке here три года назад, но, похоже, не было исправления.Могу ли я изменить строки кода в загруженном модуле в Perl?

Ну, в последнее время я считаю, если я просто закомментируйте следующие строки кода в Tag.pm, один из модулей зависимостей FLV::Info «s так:

=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(); 

"overide умереть, чтобы не умереть" подход также работает

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 :: синтаксический анализ подпрограммой и закомментирована строки кода именно так, как я изменил первоначальный 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 

FINAL UPDATE

Я понял, почему «переопределить» подход не удалось, включив строгую и предупреждающую прагму. Спасибо @Schwern за напоминание :)

Сначала введите следующие строки кода (скопированные из модуля FLV :: Util), а затем выполните переопределение подпрограммы FLV :: Tag :: parse.

Readonly::Hash our %TAG_CLASSES => (
    8 => 'FLV::AudioTag', 
    9 => 'FLV::VideoTag', 
    18 => 'FLV::MetaTag', 
); 
+1

Я покрываю некоторые из этих материалов в глубину в _Mastering Perl_. –

ответ

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, сделать строку заменить на код, и Eval его обратно.

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

+0

@Schwern, спасибо, что поделились мной с этими «плохими идеями»! Очень полезно :) – Mike

+0

Ответ должен быть помечен для следующей дискуссии о том, что именно означает «динамическое» свойство Perl. – Dummy00001

3

OK, ответ Schwern был довольно тщательно, но вот один подход, который будет меньше «плохо» ...

Go со своим вторым подходом (вырезать & вставить всю процедуру в ваш код и ввести его после загрузки оригинал) ... НО ... условие это на конкретной версии FLV::Info (или FLV::Tag).

Таким образом, у вас все еще есть патч для обезьян (это технически считается патчем обезьяны?), Но вы удаляете одну из самых больших причин, по которой этот подход считается «плохим», а именно, что любое обновление до модуля возможно, столкнуться с вашей настраиваемой подпрограммой. Если вы защитите перезапись при проверке версии, вы устраните эту проблему.

+1

I concur.123456 – Schwern

Смежные вопросы