Thread FFmpeg::Stream::Video installieren (20 answers)
Opened by cbxk1xg at 2010-10-05 18:16

topeg
 2010-10-10 17:06
#141799 #141799
User since
2006-07-10
2611 Artikel
BenutzerIn

user image
So das ganze nochmal etwas anders:

Code (perl): (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#!/usr/bin/perl
use strict;
use warnings;
use utf8;
use Glib qw(TRUE FALSE);
use GStreamer -init;

# eingabe datei
my $file='test.avi';
# ausgabe datei
my $ofile='test.jpg';
# position im video
my $pos=3_000_000_000; # ns
my $scale_w=720;
my $scale_h=210;

my @scales=('video/x-raw-yuv');
push(@scales,"width=$scale_w") if($scale_w);
push(@scales,"height=$scale_h") if($scale_h);

#pipeline erzeugen
my $pipeline = GStreamer::Pipeline->new("my_pipeline");
my( $src, $decoder,
    $vcolor, $vscale, $vcaps, $vjpeg, $vsink,
    $aqueue, $asink ) =
  GStreamer::ElementFactory -> make(
    filesrc          => "data_src",
    decodebin        => "data_decoder",

    ffmpegcolorspace => "video_colorspace",
    videoscale       => "video_scale",
    capsfilter       => "video_caps",
    jpegenc          => "video_jpeg",
    fakesink         => "video_sink",

    queue            => "audio_queue",
    fakesink         => "audio_sink",
  );

$pipeline->add($src, $decoder, $vcolor, $vscale, $vcaps, $vjpeg, $vsink, $aqueue, $asink);

$src   ->link($decoder);
$vcolor->link($vscale, $vcaps, $vjpeg, $vsink);
$aqueue->link($asink);

$vcaps->set('caps', GStreamer::Caps->from_string(join(', ',@scales)));

$pipeline->get_by_name('data_decoder')->signal_connect(pad_added => sub{
    my $element=shift;
    my $pad=shift;

    my $type='unknown';
    $type=lc($1) if($pad->get_caps()->to_string()=~m!^([^/]+)!);

    if($type eq 'video' || $type eq 'image')
    {
      $pad->link($vcolor->get_pad('sink'));
      #print "link ".$element->get_name()." video (".$pad->get_name().") to ".$vcolor->get_name()."\n";
    }
    elsif($type eq 'audio')
    {
      $pad->link($aqueue->get_pad('sink'));
      #print "link ".$element->get_name()." audio (".$pad->get_name().") to ".$aqueue->get_name()."\n";
    }
    else
    { print "not link $type from ".$pad->get_name()."\n"; }
  });
$src->set('location' => $file);

# Puffer für die Ausgabe
my $buffer=undef;

# Listener für Frame einrichten
$vsink->set('signal-handoffs' => TRUE);
$vsink->signal_connect(handoff => sub{
    return TRUE if($buffer);

    my $query=GStreamer::Query::Position->new('time');
    $vsink->query($query);
    my (undef, $pos_now)=$query->position;

    return TRUE if($pos_now<$pos);

    $buffer=$_[1];

    return TRUE;
  });

# video abspielen
$pipeline->set_state("playing");
my $seek=1;
my $bus=$pipeline->get_bus();
while (1)
{
  # alle nahrichten vom bus holen
  my $message = $bus -> poll("any", 0);


  # wenn keine Nachrichten warten
  unless (defined $message)
  {
    # Schleife beenden wenn wir ein Bild haben
    last if($buffer);

    select(undef,undef,undef,0.25);
    #print "WAIT ".time()."\n";
    next;
  }


  # beenden wenn das Video am Ende ist,
  # oder ein Fehler auftrat
  last if ( $message->type & "eos");
  if ( $message->type & "error"   || $message->type & "warning" )
  {
    print "ERROR:\n";
    print $message->error()."\n";
    print $message->debug()."\n";
    last;
  }


  # wenn sich det Zusand eines pileelemts geändert hat
  if($message->type & "state-changed")
  {
    # Name des Elementes ermitteln
    my $name=$message->src()->get_name() || 'UNDEF';
    #print "$name => ".$message->new_state()."\n";

    # es wurde noch nicht zu der stelle gespringen,
    # der Name stimmt und die pipeline ist "playing"
    if($seek && $name eq 'my_pipeline' && $message->new_state() eq 'playing')
    {
      # zur gewünschten Stelle springen
      $pipeline->seek(1,'time','flush','set',$pos,'none',0) || last;
      $seek--;
    }
  }
}
# pipeline stoppen
$pipeline->set_state("null");

# wenn ein bild da ist
if($buffer)
{
  # bild in Datei schreiben.
  #open(my $fh, '>', $ofile) or die("Error open $ofile! ($!)");
  open(my $fh, '| display -') or die("Error open $ofile! ($!)");
  binmode($fh);
  print $fh $buffer->data();
  close($fh);
}


Aber ich hatte damit ganz ordentlich zu kämpfen. Das Problem war, dass GStremer intern durcheinander kam als ich "parsebin" als reinen Videodecoder einsetzte. Parsebin hat zwei ausgehenden "Pad"s und das habe ich nicht beachtet. Dadurch scheint GStreamer einige Daten und Referenzen zu früh zu löschen, sodass es bei mir immer wieder zu abstürzen kam. Da GStreamer intern über Threads arbeitet taten diese Fehler nur sporadisch und kaum wiederholbar und in einer breiten Variation an jeder Stelle des Codes auf.
Wird die Pipeline über GStreamer::parse_launch gestartet, so werden die Plugins scheinbar ein ganz klein wenig anders behandelt und der Code war robuster. Leider gab es immer noch Probleme wenn man als Eventloop "Glib::MainLoop" nutzte. Das sollte kein Problem sein, wenn man die Plugins "korrekt" einsetzt.

View full thread FFmpeg::Stream::Video installieren