2010-09-06 10 views
5

Cuando uso el módulo FLV::Info para extraer metadatos o combinar varios archivos FLV, recibo con frecuencia un error de "Tamaño de etiqueta demasiado pequeño" y luego el módulo simplemente se niega a trabajar. Alguien emitió un informe de error here hace tres años, pero no parece haber una solución.¿Puedo cambiar las líneas de código en un módulo cargado en Perl?

Bueno, hace poco me encuentro si simplemente comente las siguientes líneas de código en Tag.pm, uno de los módulos de dependencia FLV::Info 's de este modo:

=pod 
if ($datasize < 11) 
    { 
     die "Tag size is too small ($datasize) at byte " . $file->get_pos(-10); 
    } 
=cut 

FLV::Info continuación, va a hacer el trabajo fácilmente como se esperaba .

No estoy seguro si esto es una pregunta muy tonta, pero me siento curiosidad:

¿Hay una manera sencilla de cambiar un par de líneas de código en un módulo cargado sin modificar el archivo .pm original?

¿Alguna idea, sugerencia o comentario? Gracias como siempre :)

ACTUALIZACIÓN

Muchas gracias a @Shwern. Su respuesta es muy satisfactoria :) También gracias a @DVK por la sugerencia y el término "parche de mono" y @brian por la recomendación del libro.

Aquí está mi opinión sobre las pruebas en un archivo FLV de muestra que me arrojaría el error "El tamaño de la etiqueta es demasiado pequeño" si utilizo el módulo original sin hacer nada al respecto.

El enfoque "eval nuevo" resuelve el problema

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

El "Die overide para no morir" enfoque también funciona

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

El enfoque de "redefinir", sin embargo, no funciona como esperaba.

que copiar y pegar la etiqueta FLV originales :: :: subrutina de análisis y comentado las líneas de código exactamente la forma en que modifican el archivo Tag.pm original de este modo:

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

pero tengo este error:

Unknown tag type 18 at byte 13 (0xd) 

Bueno, incluso si copiar y pegar exactamente la misma subrutina de análisis sin ninguna modificación en mi nueva definición, que estoy recibiendo el error "Desconocido tipo de etiqueta" en lugar de "tamaño de la etiqueta es demasiado pequeña".

¡Esto es extraño!

Como referencia, el "eval de nuevo" y "anular morir para no morir" enfoques me darán los siguientes:

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 

INFORME FINAL DE ACTUALIZACIÓN

que he descubierto por qué el el enfoque "redefinir" falló al activar el pragma estricto y las advertencias. Gracias a @Schwern para el recordatorio :)

Agregue las siguientes líneas de código (copiadas del módulo FLV :: Util) primero y luego realice la redefinición de la subrutina FLV :: Tag :: parse.

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

Cubro algunas de estas cosas en profundidad en _Mastering Perl_. –

Respuesta

18

¿Simple? No. Pero hay algunas cosas locas que puedes hacer. Aquí hay algunas malas ideas.

Una de las más obvias es poner una copia pirateada del archivo .pm en su proyecto, en algún lugar para que se vea antes de la versión del sistema.

Otro es similar, pero para cortar & pegue toda la rutina en su código e inyéctelo después de cargar el original.

use FLV::Tag; 

no warnings 'redefine'; 
*FLV::Tag::parse = sub { 
    ...copy of FLV::Tag::parse with your edits... 
}; 

Se podría anular die para no morir cuando ve ese mensaje.

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... 
} 

Se podría volcar el contenido de dicha subrutina de nuevo en Perl, hacer una cadena reemplazar en el código, y eval de nuevo.

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; 

O usted podría recorrer el árbol de código de operación de dicha subrutina y cambiar la condición, que lo dejo como ejercicio para alguien con más tiempo en sus manos.

Todas son malas ideas. Es mejor que simplemente cambie el código en su lugar y vuelva a ponerse en contacto con el autor para informarle que hay nueva información.

+0

Esta es una gran respuesta, con un gran descargo de responsabilidad :) – Konerak

+0

@Schwern, ¡gracias por compartirme esas "malas ideas"! Muy útil :) – Mike

+0

La respuesta debe estar marcada para la próxima discusión sobre qué significa exactamente propiedad "dinámica" de Perl. – Dummy00001

3

bien, la respuesta de Schwern era bastante profunda, pero aquí es un enfoque que será menos "malo" ...

ir con su segundo enfoque (CUT & pegar toda la rutina en el código e inyectarlo después de la carga el original) ... PERO ... condicionarlo en la versión específica de FLV::Info (o FLV::Tag).

De esta manera, todavía tiene su parche de mono (¿esto se considera técnicamente un parche de mono?) PERO elimina una de las principales razones por las que este enfoque sería considerado "malo", es decir, cualquier actualización al módulo posiblemente choque con su subrutina parcheada personalizada. Si protege la sobreescritura con la verificación de versión, elimina esa preocupación.

+1

Estoy de acuerdo.123456 – Schwern

Cuestiones relacionadas