Steve_BZ has asked for the wisdom of the Perl Monks concerning the following question:

Hi All

I need to copy the hash created by FFmpeg::Command to an array of hashes to form a queue that I can then shift one at a time for FFmpeg to process in an orderly fashion, avoiding the overloading of my CPU from the users repeated calls.

However when I do this each element of the hash becomes one element of the array, when what I want is each hash to be a single array element. I tried:

push @gl_thread_queue_arr, %{$ffmpeg};

I don't have access to %ffmpeg, because this was defined by FFmpeg::Command. When I try to use %ffmpeg, I get 'variable not defined', etc.

Any ideas much appreciated

Regards

Replies are listed 'Best First'.
Re: Copying a hash to an array of hashes.
by kennethk (Abbot) on Sep 18, 2009 at 21:44 UTC
    It looks like $ffmpeg is a hash reference. Adding to your queue is therefore as easy as

    push @gl_thread_queue_arr, $ffmpeg;

    The way you were invoking it before puts a hash in list context, which is to say it flattens it as you observed. If you want to access the hashref as a hash, you'd use the syntax

    %$ffmpeg

    or

    $ffmpeg->{key}

    You'd probably do well to read the Perl reference reference literature, such as perlref, perlreftut and perllol.

      Thanks for your reply.

      It's true, you're right. But why? I looked in my trusty Ellie Quigley, and maybe I wasn't looking in the right place, but I couldn't see anything there.

      I would expect this to provide an array of references, but it doesn't.

      Anyway it works.

      Thanks.

        Perl references are a bit unintuitive, especially when combined with list context.

        The main problem you're running into is that putting a hash in list context gives you a flat list containing the keys and values of the hash interleaved, so:

        my %array = ( a => 1, b => 2); my @list = %array;
        Now @list (*) will contain either ('a',1,'b',2) or ('b',2,'a',1)

        Just like

        my @list1 = (1,2); my @list2 = (@array,3,4);
        @list2 will contain (1,2,3,4).

        But references will not be subject to flattening.

        I strongly recommend the Camel book for anyone interested in the truth on perl, straight from the horse's mouth. Especially if they're already past the easy stuff. It's not the easiest book but it reads well and for an all round reference and "concept" book on perl, you can't beat it.

        (*) please don't ask me about the differences between lists and arrays, it's subtle. forget it for now.

      Hi Kenneth

      I'm using the code you suggested, but I'm not sure that I'm getting what I need. The code I have is:

          push @gl_thread_queue_arr, $ffmpeg;    # Add job to Ffmpeg queue

      This creates $gl_thread_queue_arr[0] in the shape of %ffmpeg, which is perfect. Then I start to create the next ffmpeg job. The first thing is that the output file changes, so I reflect that in %ffmpeg with:

          $ffmpeg->output_file('media\a_' . $gl_id_txt . "_" . $gl_examination_dte . "_" . $gl_edit_mode . '.avi');

      and now $gl_thread_queue_arr[0] changes to the new file name, which is not right. What am I doing wrong?

      Interestingly, if I setup both the jobs in separate subroutines, so that $ffpmeg goes out of scope between job setups, there is no problem.

      Regards

        The issue you are encountering is that the first element of @gl_thread_queue_arr is still a reference to the same object you are now modifying. If you want to create a new ffmpeg job, you need to create a new ffmpeg object to reference it. Something like this (built off the FFmpeg::Command example, untested):

        use FFmpeg::Command; my @gl_thread_queue_arr = (); while (<DATA>) { my @options = split; my $ffmpeg = FFmpeg::Command->new('/usr/local/bin/ffmpeg'); $ffmpeg->input_options({ file => $options[0], }); # Set timeout $ffmpeg->timeout(300); # Convert a video file into iPod playable format. $ffmpeg->output_options({ file => $options[1], device => 'ipod', }); push @gl_thread_queue_arr, $ffmpeg; # Add job to Ffmpeg queue } foreach my $ffmpeg (@gl_thread_queue_arr) { my $result = $ffmpeg->exec(); croak $ffmpeg->errstr unless $result; } __DATA__ input_file1 output_file1 input_file2 output_file3 input_file3 output_file2