2012-12-22 58 views
26

Abbiamo bisogno di dividere un grande feed video WMV live in piccoli pezzi della stessa dimensione. Abbiamo creato uno script che funziona bene, tranne che per una cosa: i blocchi video non iniziano con un fotogramma chiave, quindi quando si riproducono gran parte dei video non visualizzano alcuna immagine finché un fotogramma chiave del video originale non viene visualizzato raggiunto.Come dividere un video usando FFMPEG in modo che ogni blocco inizi con un fotogramma chiave?

Non c'è un modo per dire a ffmpeg di fare in modo che il video di uscita inizi con un fotogramma chiave?

Ecco come le nostre linee di comando guardano in questo momento:

ffmpeg.exe -i "C:\test.wmv" -ss 00:00:00 -t 00:00:05 -acodec copy -vcodec copy -async 1 -y "0000.wmv" 
ffmpeg.exe -i "C:\test.wmv" -ss 00:00:05 -t 00:00:05 -acodec copy -vcodec copy -async 1 -y "0001.wmv" 

e così via ...

+0

Stai utilizzando una build recente di ffmpeg? – LordNeckbeard

+0

Sì l'ultimo: versione ffmpeg N-47062-g26c531c. Abbiamo anche provato a utilizzare -force_key_frames: v 00:00:00 o -force_key_frames: v 00:00:05, ma non fa differenza nell'output. Credo che l'uso di -vcodec copy in realtà significhi solo copiare gruppi di immagini nel nuovo file, ma non ci sarà alcun tipo di reencoding per aggiungere fotogrammi chiave. È il caso? – sboisse

+0

'-vcodec copy' non ri-codifica: esegue solo il demuxing e il muxing. – LordNeckbeard

risposta

20

L'ultima build di FFMPEG includono una nuova opzione "segmento" che fa esattamente quello che penso che vi serve.

ffmpeg -i INPUT.mp4 -acodec copy -f segment -vcodec copy -reset_timestamps 1 -map 0 OUTPUT%d.mp4

Questo produce una serie di file di output numerate che vengono suddivisi in segmenti in base al key frame. Nel mio test, ha funzionato bene, anche se non l'ho usato per più di qualche minuto e solo in formato MP4.

+4

Altri esempi: https://www.ffmpeg.org/ffmpeg-formats.html#Examples-5 – zanetu

3

Se siete disposti a fare un po 'di scripting e volete I fotogrammi a un particolare intervallo l'unico modo per farlo è

  1. Run ffprobe e raccogliere le posizioni della i telai dall'uscita

    ffprobe -show_streams

  2. Esegui una serie di comandi -ss -t utilizzando lo stesso script per ottenere i blocchi desiderati.

È quindi possibile avere il vostro script decidere minimo numero di frame [dicono che ci sono due immagini I entro 10 fotogrammi l'uno dall'altro, è davvero non si vuole essere la suddivisione in blocchi lì].

L'altro modo per farlo è quello di utilizzare multisfilesink di gstreamer e impostare la modalità di fotogramma chiave [tuttavia che la volontà pezzo ad ogni fotogramma chiave in modo che non può essere l'ideale]

5

Usa ffprobe -show_frames -pretty <stream> per identificare i fotogrammi chiave.

+0

Sembra una soluzione promettente. Indagherà. Ho controllato la documentazione ma non ho trovato facilmente un modo per ffprobe una piccola sezione del video (diciamo, solo i primi 10 secondi). Poiché il video è piuttosto lungo, genera una quantità di output MB che richiede diversi secondi per essere generata. Ci sono opzioni in ffprobe per l'output di frame di un particolare stream solo e all'interno di un intervallo particolare? – sboisse

+0

ffprobe -show_frames ** - select_streams v ** filtrato solo sui fotogrammi video. Ora avrei solo bisogno di specificare un intervallo di frame o un intervallo di tempo. – sboisse

+0

Ho esaminato la documentazione e non ho trovato nulla per limitare un intervallo di tempo particolare del video. Quindi suppongo che userò ffmpeg per ottenere un chunk, quindi chiamo ffprobe su di esso per ottenere i fotogrammi chiave, e quindi richiamerò ffmpeg su di esso per fare lo split finale appena prima di un key frame. – sboisse

17

Ecco la soluzione che avrei potuto ottenere al lavoro:

Come suggerito da AV501 e d33pika, ho usato ffprobe per trovare dove i fotogrammi chiave sono. Perché ffprobe è molto prolisso e può richiedere diversi secondi o addirittura minuti per emettere tutti i fotogrammi chiave e non v'è alcun modo per scopo l'intervallo di fotogrammi che vogliamo da un lungo video, procedo in 5 fasi:

  1. Export un video pezzo dal file originale, circa il doppio della dimensione del blocco desiderato.

    ffmpeg -i source.wmv -ss 00:00:00 -t 00:00:06 -acodec copy -vcodec copy -async 1 -y 0001.wmv 
    
  2. Utilizzare ffprobe per trovare dove sono i fotogrammi chiave. Scegli il fotogramma chiave più vicino dopo la dimensione desiderata del blocco.

    ffprobe -show_frames -select_streams v -print_format json=c=1 0001.wmv 
    
  3. Dalla uscita ffprobe ottenere il pkt_dts_time del telaio appena prima che fotogramma chiave.

  4. ffmpeg sulla parte esportata del passaggio 1, che specifica lo stesso file di input e output e che specifica -ss 00:00:00 e -t [valore trovato nel passaggio 3].

    ffmpeg -i 0001.wmv -ss 00:00:00 -t 00:00:03.1350000 -acodec copy -vcodec copy -async 1 -y 0001.wmv 
    
  5. riavvio al passo 1, utilizzando -ss [somma cumulata dei valori trovate nel passaggio 3 sopra iterazioni].

Procedendo in questo modo, sono stato in grado di avere un modo efficiente e robusto per dividere il video in fotogrammi chiave.

+1

Sono stato in grado di fare lo stesso con il tuo aiuto, tuttavia, i dts/pts iniziali sono stati spostati un po '(500ms qui). La documentazione indica che se -ss è passato PRIMA -i, allora si comporta come una ricerca, e nel mio caso, ha risolto il turno. Leggi l'argomento man ffmpeg nella sezione -ss :) – tito

+1

Perché usi il fotogramma prima del fotogramma chiave invece del fotogramma chiave stesso? Ri: _ "Dall'output di ffprobe ottieni il pkt_dts_time del frame subito prima del fotogramma chiave." _ – felwithe

+0

Se non si taglia appena prima del fotogramma chiave, quel fotogramma chiave sarà incluso come ultimo fotogramma del blocco, e il prossimo blocco non inizierà con un fotogramma chiave. – sboisse

10

Utilizzando una versione più recente di ffmpeg, è possibile ottenere ciò utilizzando ffprobe e il segmento ffmpeg muxer.

1. Utilizzare ffprobe e awk per identificare i fotogrammi chiave il più vicino possibile alla lunghezza desiderata del blocco.

ffprobe -show_frames -select_streams v:0 -print_format csv **[SOURCE_VIDEO]** 2>&1 | grep -n frame,video,1 | awk 'BEGIN { FS="," } { print $1 " " $5 }' | sed 's/:frame//g' | awk 'BEGIN { previous=0; frameIdx=0; size=0; } { split($2,time,"."); current=time[1]; if (current-previous >= **[DURATION_IN_SECONDS]**){ a[frameIdx]=$1; frameIdx++; size++; previous=current;} } END { str=a[0]; for(i=1;i<size;i++) { str = str "," a[i]; } print str;}' 

Dove

  • [SOURCE_VIDEO] = percorso video che si desidera segmento
  • [DURATION_IN_SECONDS] = lunghezza del segmento desiderato in secondi

L'uscita è stringa delimitata da virgole di fotogrammi chiave.

2.Usare l'output dei fotogrammi chiave sopra come input per ffmpeg.

ffmpeg -i [SOURCE_VIDEO] -codec copia -mappa 0 -f segmento -segment_frames [OUTPUT_OF_STEP_1] ​​[SEGMENT_PREFIX] _% 03d. [SOURCE_VIDEO_EXTENSION]

Dove

  • [SOURCE_VIDEO] = percorso video che si desidera segmento
  • [OUTPUT_OF_STEP_1] ​​ = stringa delimitata da virgole di fotogrammi chiave
  • [SEGMENT_PREFIX ] = nome dell'uscita del segmento
  • [SOURCE_VIDEO_EXTENSION] = estensione del video di origine (ad esempio, mp4, mkv)
18

Come indicato sul sito ufficiale FFMPEG Docs, ha funzionato meglio per me specificare -ss timestartprima-i input_file.ext, perché stabilisce (o almeno così ho capito) l'inizio del video generato al fotogramma chiave più vicino trovato prima tua specificato timestamp.

Cambia il tuo esempio a:

ffmpeg.exe -ss 00:00:00 -i "C:\test.wmv" -t 00:00:05 -acodec copy -vcodec copy -async 1 -y "0000.wmv" 

Ho testato questo metodo di lavoro su .flv e .mp4 file.

+3

Perfetto. Ero in difficoltà nella conversione, l'inizio dei fotogrammi è rimasto bloccato. Questa risposta ha risolto il mio problema. Grazie! –

+3

Questo ha funzionato! Che soluzione semplice e non ovvia. FFmpeg 2.5, non ho più problemi di sincronizzazione audio durante il ritaglio dei video h264 – kevinf

+0

Anche se questo compromette la durata del video, ad es. il video che ho ottenuto è stato visualizzato per 8 secondi anche se ho specificato che il video dura per 5 secondi. Il video infatti finisce a 5 secondi, ma in tutti i lettori multimediali è mostrato come se fosse lungo 8 secondi. Qualche idea? – IanDess

Problemi correlati