[PYTHON] Correction des fichiers FLV avec un temps de lecture étrange

Chose que tu veux faire

Dans certains fichiers FLV, la durée de lecture était supérieure à 2 heures, même si la vidéo était d'environ 2 minutes, et la vidéo ne s'est pas bien lue. Corrigez cela pour qu'il puisse être joué proprement. FLV MetaData Injector ne semble pas être un problème, alors j'aimerais en trouver la cause et y remédier.

Cherchez la cause

Pour le moment, utilisez FLV Extract pour diviser en fichier vidéo (.264), fichier audio (.aac) et time code (.txt). En conséquence, il n'y avait aucun problème avec la vidéo et l'audio, et il semblait y avoir un problème avec la description du code temporel. Quand je l'ouvre, cela ressemble à ce qui suit.

timecode.txt


# timecode format v2
0
0
70
134
334
609
952
1358
1816
2352
2957

Le code temporel, qui augmente en millisecondes, gonfle et grossit. De plus, la valeur numérique devient parfois plus petite (le code temporel contient la valeur numérique du temps écoulé, donc elle diminue = il ne faut pas rembobiner ...).

1305237
1329824
1354481
24718
49511
74372
99290

Devinez la solution

Après avoir fait divers calculs,

Il semble que (au moins ça ressemblait à ça). Le calcul basé sur ces résultats est le suivant.

0       -> 0
0       -> 0
70      -> 70
134     -> 134
334     -> 200
609     -> 275
952     -> 343
1358    -> 406
...
1329824 -> 24587
1354481 -> 24657
24718   -> 24718
49511   -> 24793
74372   -> 24861
99290   -> 24918

Il augmente d'environ 66 (millisecondes) (un code temporel d'environ 15 images) et semble correct.

Ecrire le code

J'ai modifié ce nombre comme suit (toute langue est acceptable car il ne s'agit que d'entrée / sortie de fichier et de calcul simple)

timecode.py


import sys
import os
import datetime

filename =''

if(len(sys.argv) > 1):
	filename = os.path.abspath(sys.argv[1])
else:
    filename = 'timecode.txt'

f = open(filename)

lines = []
header = []

for i,line in enumerate(f.readlines()):
    #Séparez le code temporel de l'en-tête au début de la ligne
    if(line.startswith('#') == False):
        lines.append(int(line))
    else:
        header.append(line)

f.close()

timecodes = []
val = 0

for i,line in enumerate(lines):
 
    #0 reste tel quel
    if(line == 0):
         timecodes.append(line)
 
    #Une valeur plus petite que l'avant est estimée comme étant le code temporel correct, insérez-la telle quelle
    elif(line < lines[i-1]):
        if(timecodes[i-1] < line):
            timecodes.append(line)

        #Si indéfini (plus petit que le code temporel précédent), insérez le code temporel précédent tel quel
        else:
            timecodes.append(timecodes[i-1])

    else:
        #Si vous prenez la différence par rapport à la ligne précédente, cela semble être le code temporel correct
        val = line - lines[i-1]

        #Si le même numéro continue, définissez le numéro précédent
        if(val == 0):
            timecodes.append(timecodes[i-1])
            
        #Si la différence est inférieure à l'avant (la valeur d'origine est supposée être le code temporel correct), insérez-la telle quelle
        elif(timecodes[i-1] > val):
            timecodes.append(line)

        #Sinon, insérez diff
        else:
            timecodes.append(val)


#Faire une liste de chaînes avec un saut de ligne à la fin
output = [str(v)+'\n' for v in timecodes]


now = datetime.datetime.now()

#Insérer un en-tête de code temporel
header.extend(output)

#Enregistrez le fichier et quittez
if(len(sys.argv) > 1):
    o_file,o_ext = os.path.splitext(os.path.abspath(sys.argv[1]))
    filename = o_file + now.strftime('_%Y%m%d%H%M%S') + o_ext
else:
    filename = 'timecode_change' + now.strftime('_%Y%m%d%H%M%S') + '.txt'

with open(filename, mode='w') as f:
    f.writelines(header)

Créer un fichier vidéo qui reflète le code temporel corrigé

J'ai utilisé MKVToolNix pour combiner le code temporel modifié avec la première vidéo / audio. Lorsque j'ai lu le fichier mkv terminé, il a joué magnifiquement.

Faites-en un fichier exécutable

Utilisez pyinstaller pour en faire un fichier exécutable afin qu'il puisse être utilisé par des personnes qui n'ont pas installé python. Si vous faites glisser et déposez le fichier vers le timecode.exe terminé, le fichier de timecode modifié sera généré dans le même dossier.

$ pyinstaller --onefile timecode.py

la revue

** 1. Placez le fichier FLV cible dans Extrait FLV et divisez-le en fichiers vidéo / audio / time code. 2. Déterminez la logique de correction 3. Appliquez le processus de correction ci-dessus au fichier de code temporel 4. Placez le code temporel modifié et le fichier vidéo / audio divisé dans MKVToolNix et combinez-les pour créer un fichier mkv. 5. Jouer **

Puisqu'il est pratique de pouvoir traiter 1 à 4 ensemble, je l'ai ajouté au code ci-dessus et il est devenu comme suit.

timecode.py


###################################################################################################
#
#Le temps de lecture de FLV est étrange, donc
#Script pour modifier le fichier de code temporel extrait de FLV Extractor
#Lorsqu'il est combiné avec des fichiers vidéo et audio avec MKV toolnix en utilisant le code temporel modifié
#Si le code temporel suit cette règle, ce sera un fichier mkv qui pourra être lu correctement.
#Installez FLV Extract et MKV ToolNix, et placez le fichier exécutable dans le PATH.
#
###################################################################################################

import sys
import os
import datetime
import subprocess
import tempfile
import shutil

#===================================================================================================
#Extraction de code temporel avec extrait flv
#===================================================================================================
def flvExtract(flvFilename,outputDir):

    cmd = [
        'flvextractcl',
        '-t',
        '-v',
        '-a',
        '-d',
        outputDir,
        flvFilename,
   ]
    print(flvFilename)
    subprocess.call(cmd)

#===================================================================================================
#Lire et modifier le fichier de code temporel
#===================================================================================================
def repairTimecode(timecodeFilename):

    f = open(timecodeFilename)

    lines = []
    header = []

    for i,line in enumerate(f.readlines()):
        #Séparez le code temporel de l'en-tête au début de la ligne
        if(line.startswith('#') == False):
            lines.append(int(line))
        else:
            header.append(line)

    f.close()

    timecodes = []
    val = 0

    for i,line in enumerate(lines):
    
        #0 reste tel quel
        if(line == 0):
            timecodes.append(line)
    
        #Une valeur plus petite que l'avant est estimée comme étant le code temporel correct, insérez-la telle quelle
        elif(line < lines[i-1]):
            if(timecodes[i-1] < line):
                timecodes.append(line)

            #Si indéfini (plus petit que le code temporel précédent), insérez le code temporel précédent tel quel
            else:
                timecodes.append(timecodes[i-1])

        else:
            #Si vous prenez la différence par rapport à la ligne précédente, cela semble être le code temporel correct
            val = line - lines[i-1]

            #Si le même numéro continue, définissez le numéro précédent
            if(val == 0):
                timecodes.append(timecodes[i-1])
            
            #Si la différence est inférieure à l'avant (la valeur d'origine est supposée être le code temporel correct), insérez-la telle quelle
            elif(timecodes[i-1] > val):
                timecodes.append(line)

            #Sinon, insérez diff
            else:
                timecodes.append(val)
        
        if(i > 0 and timecodes[i] < timecodes[i-1]):
            print(timecodes[i-1])
            print(timecodes[i])
            print('\n')

    #Faire une liste de chaînes avec un saut de ligne à la fin
    output = [str(v)+'\n' for v in timecodes]

    #Insérer un en-tête de code temporel
    header.extend(output)

    #Enregistrer le fichier en écrasant
    with open(timecodeFilename, mode='w') as f:
        f.writelines(header)

    return

#===================================================================================================
#Combinez vidéo, audio et code temporel modifié avec MKVToolNIX
#===================================================================================================
def mkvMerge(video,audio,timecode,outputDir):

    now = datetime.datetime.now()
    basename = os.path.splitext(os.path.abspath(video))[0]
    mkv = basename + now.strftime('_%Y%m%d%H%M%S') + '.mkv'

    #Utilisez mkvmerge
    #Créer mkv en multiplexant divers fichiers
    cmd = [
        'mkvmerge',
        '-o',
        mkv,
        '--timecodes',
        '0:' + timecode,
        video,
        audio
    ]

    subprocess.call(cmd)

    #Sortie vers le répertoire spécifié
    shutil.move(mkv,outputDir)

    return

#===================================================================================================
#Traitement principal
#===================================================================================================
def main():

    flvFilename = ''

    #Obtenir le nom du fichier
    if(len(sys.argv) > 1):
        flvFilename = os.path.abspath(sys.argv[1])
    else:
        flvFilename = os.path.join(os.getcwd(),'test.flv')

    #Préparez trois noms de fichier: vidéo, audio et code temporel
    o_file = os.path.splitext(os.path.basename(flvFilename))[0]
    outputDir = os.path.dirname(os.path.abspath(flvFilename))

    #Créer un répertoire temporaire pour le travail
    with tempfile.TemporaryDirectory() as tmpDir:

        print(tmpDir+'\n')

        #Extrait avec extrait FLV
        flvExtract(flvFilename,tmpDir)

        timecodeFilename = os.path.join(tmpDir,o_file + '.txt')
        videoFilename = os.path.join(tmpDir,o_file + '.264')
        audioFilename =os.path.join(tmpDir,o_file + '.aac')

        #Réparation de code temporel et création de MKV si des fichiers vidéo / audio / code temporel existent
        if(os.path.exists(timecodeFilename) and os.path.exists(videoFilename) and os.path.exists(audioFilename)):

            repairTimecode(timecodeFilename)
            mkvMerge(videoFilename,audioFilename,timecodeFilename,outputDir)
 

            #Pour le fichier FLV utilisé pour le traitement, ajoutez un horodatage au nom du fichier et déplacez-le dans le répertoire FLV directement en dessous.
            os.makedirs(os.path.join(outputDir,'flv'),exist_ok=True)

            flv = datetime.datetime.now().strftime('%Y%m%d%H%M%S') + '_' + os.path.splitext(os.path.basename(flvFilename))[0] + '.flv'
            shutil.move(flvFilename,os.path.join(os.path.join(outputDir,'flv'),flv))
 

main()

en conclusion

C'était une bonne solution, et je n'ai pas pu la résoudre en premier lieu, et il y avait certaines choses que je ne pouvais pas aider parce que le code temporel était tout à 0. (Il est possible de calculer la longueur à partir de la longueur du fichier audio, de la diviser par le nombre d'images vidéo et de produire un code temporel provisoire, mais bien sûr, l'écart sonore sera important)

Cependant, si vous rencontrez un problème dans la même situation, il est recommandé de regarder le code temporel pour le moment.

Recommended Posts

Correction des fichiers FLV avec un temps de lecture étrange
Comment mesurer le temps de lecture d'un fichier mp3 avec python
Télécharger des fichiers avec Django
Correction de la base de données avec pytest-docker