/ FFMPEG pour genérer des thumbnails
FFMPEG pour genérer des thumbnails
496x
Facebook Twitter Google+
 

FFMPEG et thumbnails

Voici un script pour générer des thumbnails en utilisant FFMPEG. Il sert à détecter les écrans noirs, écran figés et indique s'il n'y plus de stream, en comparant deux images de deux instants différents. Dans ce cas-ci, FFMPEG est utilisé pour écouter les données d'un SPTS UDP et analyzer ce qu'il reçoit. Idéalement, pour stocker les résultats, c'est mieux d'utiliser une base de données pour analyser plus facilement les résulats, avoir un historique et ainsi de suite... pour simplifier le cas on utilise pas de base de données dans notre cas. Vous remarquerez que la section qui prends les données (input) a été encore plus simplifié... on lit une entrée quelconque (non définie) et ce sera à vous d'en considérer la source réelle.

Ce script est fait pour s'exécuter automatiquement dans un crontab à intervalle régulière, il contient donc une vérification pour s'assurer de ne pas l'exécuter plus d'une fois. Il utilise aussi l'utilitaire ImageMagick ce qui simplifie grandement la tâche. Si vous n'avez jamais utiliser cet outil et que vous devez modifier des images, vous allez adorer!

Ce script lance plusieurs threads par groupe de 6 par exécution à la fois, selon le nombre que vous voulez "scanné" ce chiffre peut être changé;. N'oublier pas que si vous exécuter une lecture sur 10 SPTS de 10Mbps vous êtes déjà à 100Mbps, il vaut donc mieux avoir une bonne carte réseau! Un autre point important à considérer avec ce script, c'est qu'il est préférable de join de façon permanente les streams... autrement si votre routeur n'est pas très sympathique, il risque de trouver la charge un peu lourde avec tout ces join et leave fréquents (encore là, tout dépends du nombre de SPTS que vous avez à scanner!).

Un dernier point important à considérer c'est la taille qu'on indique à ffmpeg pour qu'il considère son analyze terminée... si le chiffre est trop gros le script sera trop lent et s'il est trop petit, vous aurez des alertes de "no stream", il vaut donc mieux personnaliser cette partie en fonction de votre réseau et de vos streams!

Pour le reste, je vous laisse découvrir le script!

thumbnails.py

  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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
#!/usr/bin/env python
import os
import os.path
import subprocess
import threading
import re
import socket
import datetime

s_dir = "/tmp/channels/"


def is_color(filen):
    cmd = subprocess.Popen(["convert", filen, "-colors", "16", "-depth",
                            "8", "-format", "%c", "histogram:info:-"],
                           stdout=subprocess.PIPE, stderr=subprocess.PIPE)

    out = cmd.stdout.read()

    m_line = re.compile(r"""\s*(?P<count>\d+):\s*\(\s*(?P<R>\d+),\s*(?P<G>
                        \d+),\s*(?P<B>\d+).+""")

    colors = []
    diffs = []

    for line in out.splitlines():
        t_line = m_line.search(line)
        if t_line:
            color = [int(x) for x in (t_line.group('count'),
                                      t_line.group('R'),
                                      t_line.group('G'),
                                      t_line.group('B'),
                                      )
                     ]
            colors.append(color)
    # sort
    colors.sort(reverse=True, key=lambda x: x[0])

    # logging.debug(colors)
    for color in colors:
        # Calculate the mean differences between the RGB components
        # Shades of grey will be very close to zero in this metric...
        diff = float(sum([abs(color[2] - color[1]),
                     abs(color[3] - color[1]),
                     abs(color[3] - color[2]), ])) / 3
        if diff >= 1.0:
            diffs.append(diff)
        elif diff == 0 and len(colors) > 1 and (color[3] > 100):  # zone of gray
            diffs.append(diff)

    return len(diffs) > 1


def get_lock(process_name):
    global lock_socket   # Without this our lock gets garbage collected
    lock_socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
    b_rtn = False

    try:
        lock_socket.bind('\0' + process_name)
        b_rtn = True
    except socket.error:
        print('Already running')

    return b_rtn


def thd_ffmpeg(chan_list):
    for channel in chan_list:
        b_black, b_freeze, b_stream = (False, False, True)

        spts = channel['spts']
        hd = channel['hd']
        port = channel['port']

        # time.sleep(0.450)
        # "-probesize", "550000" if hd else "250000",
        # "-analyzeduration", "1500000",
        # if os.path.isfile(s_dir + str(channel_id) + '.dat'):
        #    os.remove(s_dir + str(channel_id) + ".dat")

        if os.path.isfile(s_dir + spts + '.jpg'):
            os.rename(s_dir + spts + ".jpg", s_dir + spts + "-1.jpg")
        else:
            if os.path.isfile(s_dir + spts + '-1.jpg'):
                os.remove(s_dir + spts + "-1.jpg")

        i_probesize_sd = 250000
        i_probesize_hd = 550000
        i_count = 0

        while (not os.path.isfile(s_dir + spts + '.jpg')) and i_count < 3:
            subprocess.call(["ffmpeg", "-sn", "-an", "-dn", "-f", "mpegts",
                             "-probesize",
                             str(i_probesize_hd) if hd else str(i_probesize_sd),
                             "-timeout", "5000", "-i", "udp://" + spts + ":" +
                             str(port), "-vf", "thumbnail=5", "-y",
                             "-vframes", "1", "-s", "192x144",
                             s_dir + spts + ".jpg"],
                            stdout=FNULL, stderr=FNULL)

            i_count += 1
            i_probesize_sd += 300000
            i_probesize_hd += 400000

        if os.path.isfile(s_dir + spts + '.jpg'):
            if not is_color(s_dir + spts + ".jpg"):
                b_black = True

            if os.path.isfile(s_dir + spts + '-1.jpg'):
                # "compare", "-metric", "AE", "-fuzz", "10%", (...)
                cmd = subprocess.Popen(["compare", "-metric", "RMSE",
                                        s_dir + spts + '.jpg',
                                        s_dir + spts + '-1.jpg', "/dev/null"],
                                       stdout=subprocess.PIPE,
                                       stderr=subprocess.PIPE)

                output = cmd.stderr.read()
                output = re.search('%s(.*)%s' % ('\(', '\)'), output).group(1)

                if float(output) < 0.03:  # isdigit() doesn't work with float
                    b_freeze = True
        elif not os.path.isfile(s_dir + spts + '-1.jpg'):
            b_stream = False

        if b_black or b_freeze or not b_stream:
            f_curr = None
            f_prev = None

            if os.path.isfile(s_dir + spts + '.jpg'):
                f_curr = open(s_dir + spts + '.jpg', "rb")
            
            if os.path.isfile(s_dir + spts + '-1.jpg'):
                f_prev = open(s_dir + spts + '-1.jpg', "rb")

            print("IMG : " + spts + " Black : " + str(b_black) + " Freeze : " +
                  str(b_freeze) + " Stream : " + str(b_stream))

            # pg8000.Binary(zlib.compress(f_curr.read(), 9))
            
            if f_curr is not None:
                f_curr.close()

            if f_prev is not None:
                f_prev.close()


def execute_monitor():
        results = list()        # get your list from a file, db or whatever...
                                # containing at least the multicast IP and port
        i_index = 0
        channel_list = []
        thread = []

        for i_cnt, row in enumerate(results):
            h_data = {'spts': row[0], 'port': row[1],
                       'hd': row[2], 'monitor': row[3]}

            if h_data['monitor']:
                channel_list.append(h_data)
                i_index += 1

            if 0 == (i_index % 6) or i_cnt == (len(results) - 1):
                # time.sleep(2)
                t = threading.Thread(target=thd_ffmpeg, args=[channel_list])
                thread.append(t)
                t.start()

                channel_list = []

        for t in thread:
            t.join()


if __name__ == "__main__":
    if get_lock('thumbnails.py'):
        dt_start = datetime.datetime.now()
        FNULL = open(os.devnull, 'w')
        open(s_dir + "gen.dat", 'w').close()

        s_result = 'OK'

        try:
            execute_monitor()
        except Exception as e:
            s_result = str(e)

        print("Execution finished : " + int((datetime.datetime.now() -
                                             dt_start).total_seconds() * 1000))

        os.remove(s_dir + "gen.dat")

Loading... Veuillez patienter...