2012-02-12 20 views
15

Estoy tratando de grabar la secuencia RTSP desde la cámara Axis con FFmpeg libavformat. Puedo tomar videos de archivos y luego guardarlos en otro archivo, esto está bien. Pero la cámara envía datos extraños, FPS es 100 y la cámara envía cada cuarto fotograma, por lo que el resultado FPS es de aproximadamente 25. Pero libavformat establece paquetes dts/pts para 90000 fps (¿predeterminado?) Y la nueva secuencia de archivos tiene 100 fps. El resultado es una hora de video con solo 100 cuadros.Grabar secuencia RTSP con FFmpeg libavformat

Aquí está mi código

#include <stdio.h> 
#include <stdlib.h> 
#include <libavcodec/avcodec.h> 
#include <libavformat/avformat.h> 
#include <libavformat/avio.h> 


int main(int argc, char** argv) { 

    AVFormatContext* context = avformat_alloc_context(); 
    int video_stream_index; 

    av_register_all(); 
    avcodec_register_all(); 
    avformat_network_init(); 

    //open rtsp 
    if(avformat_open_input(&context, "rtsp://195.200.199.8/mpeg4/media.amp",NULL,NULL) != 0){ 
     return EXIT_FAILURE; 
    } 

    if(avformat_find_stream_info(context,NULL) < 0){ 
     return EXIT_FAILURE; 
    } 

    //search video stream 
    for(int i =0;i<context->nb_streams;i++){ 
     if(context->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) 
      video_stream_index = i; 
    } 

    AVPacket packet; 
    av_init_packet(&packet); 

    //open output file 
    AVOutputFormat* fmt = av_guess_format(NULL,"test2.avi",NULL); 
    AVFormatContext* oc = avformat_alloc_context(); 
    oc->oformat = fmt; 
    avio_open2(&oc->pb, "test.avi", AVIO_FLAG_WRITE,NULL,NULL); 

    AVStream* stream=NULL; 
    int cnt = 0; 
    //start reading packets from stream and write them to file 

    av_read_play(context);//play RTSP 
    while(av_read_frame(context,&packet)>=0 && cnt <100){//read 100 frames 
     if(packet.stream_index == video_stream_index){//packet is video    
      if(stream == NULL){//create stream in file 
       stream = avformat_new_stream(oc,context->streams[video_stream_index]->codec->codec); 
       avcodec_copy_context(stream->codec,context->streams[video_stream_index]->codec); 
       stream->sample_aspect_ratio = context->streams[video_stream_index]->codec->sample_aspect_ratio; 
       avformat_write_header(oc,NULL); 
      } 
      packet.stream_index = stream->id; 

      av_write_frame(oc,&packet); 
      cnt++; 
     } 
     av_free_packet(&packet); 
     av_init_packet(&packet); 
    } 
    av_read_pause(context); 
    av_write_trailer(oc); 
    avio_close(oc->pb); 
    avformat_free_context(oc); 

    return (EXIT_SUCCESS); 
} 

archivo de resultados está aquí: http://dl.dropbox.com/u/1243577/test.avi

Gracias por cualquier consejo

+0

En su código, solo graba los primeros 100 fotogramas, ¿es esto así? – ciphor

+0

¿Cuál es el formato de la cámara? ¿Es h.264? – ransh

+0

Ver también https://stackoverflow.com/questions/10715170/receiving-rtsp-stream-using-ffmpeg-library – rogerdpack

Respuesta

0

Recientemente estaba haciendo lo mismo. Tenía FPS dos veces más bajo que la cámara enviada. El motivo estaba en AVstream-> codec-> ticks_per_frame field, establecido en 2. Mi fuente fue progresiva, y en caso de que la tuya esté entrelazada, entonces esa podría ser una razón de otro factor de 2, dando 4x FPS diferentes. 90000 Hz es la base de tiempo predeterminada para la transmisión de video enviada a través de RTSP. La base de tiempo es diferente de FPS en resolución. Por ejemplo, un cuadro con marca de tiempo 30000 se mostrará a 1/3 de segundo si la base de tiempo es 90000 Hz. La base de tiempo debe colocarse en la estructura AVstream durante la salida, pero AVFormatContext debe tener un valor real de FPS.

+0

Querido Tosha, ¿cuál es la solución? cambiar ticks_per_frame? cambiar fps? cambiar la base de tiempo? –

6

Así es como lo hago. Lo que encontré fue que al recibir H264, la velocidad de fotogramas en la transmisión no es correcta. Envía 1/90000 Timebase. Me salteo la inicialización de la nueva transmisión de la secuencia entrante y solo copio ciertos parámetros. El r_frame_rate entrante debe ser exacto si max_analyze_frames funciona correctamente.

#include <stdio.h> 
#include <stdlib.h> 
#include <libavcodec/avcodec.h> 
#include <libavformat/avformat.h> 
#include <libavformat/avio.h> 
#include <sys/time.h> 

time_t get_time() 
{ 
    struct timeval tv; 

    gettimeofday(&tv, NULL); 

    return tv.tv_sec; 
} 

int main(int argc, char* argv[]) 
{ 
    AVFormatContext *ifcx = NULL; 
    AVInputFormat *ifmt; 
    AVCodecContext *iccx; 
    AVCodec *icodec; 
    AVStream *ist; 
    int i_index; 
    time_t timenow, timestart; 
    int got_key_frame = 0; 

    AVFormatContext *ofcx; 
    AVOutputFormat *ofmt; 
    AVCodecContext *occx; 
    AVCodec *ocodec; 
    AVStream *ost; 
    int o_index; 

    AVPacket pkt; 

    int ix; 

    const char *sProg = argv[ 0 ]; 
    const char *sFileInput; 
    const char *sFileOutput; 
    int bRunTime; 

    if (argc != 4) { 
    printf("Usage: %s url outfile runtime\n", sProg); 
    return EXIT_FAILURE; 
    } 
    sFileInput = argv[ 1 ]; 
    sFileOutput = argv[ 2 ]; 
    bRunTime = atoi(argv[ 3 ]); 

    // Initialize library 
    av_log_set_level(AV_LOG_DEBUG); 
    av_register_all(); 
    avcodec_register_all(); 
    avformat_network_init(); 

    // 
    // Input 
    // 

    //open rtsp 
    if (avformat_open_input(&ifcx, sFileInput, NULL, NULL) != 0) { 
    printf("ERROR: Cannot open input file\n"); 
    return EXIT_FAILURE; 
    } 

    if (avformat_find_stream_info(ifcx, NULL) < 0) { 
    printf("ERROR: Cannot find stream info\n"); 
    avformat_close_input(&ifcx); 
    return EXIT_FAILURE; 
    } 

    snprintf(ifcx->filename, sizeof(ifcx->filename), "%s", sFileInput); 

    //search video stream 
    i_index = -1; 
    for (ix = 0; ix < ifcx->nb_streams; ix++) { 
    iccx = ifcx->streams[ ix ]->codec; 
    if (iccx->codec_type == AVMEDIA_TYPE_VIDEO) { 
     ist = ifcx->streams[ ix ]; 
     i_index = ix; 
     break; 
    } 
    } 
    if (i_index < 0) { 
    printf("ERROR: Cannot find input video stream\n"); 
    avformat_close_input(&ifcx); 
    return EXIT_FAILURE; 
    } 

    // 
    // Output 
    // 

    //open output file 
    ofmt = av_guess_format(NULL, sFileOutput, NULL); 
    ofcx = avformat_alloc_context(); 
    ofcx->oformat = ofmt; 
    avio_open2(&ofcx->pb, sFileOutput, AVIO_FLAG_WRITE, NULL, NULL); 

    // Create output stream 
    //ost = avformat_new_stream(ofcx, (AVCodec *) iccx->codec); 
    ost = avformat_new_stream(ofcx, NULL); 
    avcodec_copy_context(ost->codec, iccx); 

    ost->sample_aspect_ratio.num = iccx->sample_aspect_ratio.num; 
    ost->sample_aspect_ratio.den = iccx->sample_aspect_ratio.den; 

    // Assume r_frame_rate is accurate 
    ost->r_frame_rate = ist->r_frame_rate; 
    ost->avg_frame_rate = ost->r_frame_rate; 
    ost->time_base = av_inv_q(ost->r_frame_rate); 
    ost->codec->time_base = ost->time_base; 

    avformat_write_header(ofcx, NULL); 

    snprintf(ofcx->filename, sizeof(ofcx->filename), "%s", sFileOutput); 

    //start reading packets from stream and write them to file 

    av_dump_format(ifcx, 0, ifcx->filename, 0); 
    av_dump_format(ofcx, 0, ofcx->filename, 1); 

    timestart = timenow = get_time(); 

    ix = 0; 
    //av_read_play(context);//play RTSP (Shouldn't need this since it defaults to playing on connect) 
    av_init_packet(&pkt); 
    while (av_read_frame(ifcx, &pkt) >= 0 && timenow - timestart <= bRunTime) { 
    if (pkt.stream_index == i_index) { //packet is video    
     // Make sure we start on a key frame 
     if (timestart == timenow && ! (pkt.flags & AV_PKT_FLAG_KEY)) { 
     timestart = timenow = get_time(); 
     continue; 
     } 
     got_key_frame = 1; 

     pkt.stream_index = ost->id; 

     pkt.pts = ix++; 
     pkt.dts = pkt.pts; 

     av_interleaved_write_frame(ofcx, &pkt); 
    } 
    av_free_packet(&pkt); 
    av_init_packet(&pkt); 

    timenow = get_time(); 
    } 
    av_read_pause(ifcx); 
    av_write_trailer(ofcx); 
    avio_close(ofcx->pb); 
    avformat_free_context(ofcx); 

    avformat_network_deinit(); 

    return EXIT_SUCCESS; 
} 
+0

av_dump_format (ofcx, 0, ofcx-> filename, 1); me está dando un error. – patrick

+0

recibí el mismo error ... ¿Obtuviste alguna solución, ayúdanos a resolverlo ... gracias de antemano – Anny

3

No creo que deba aumentar el valor de PTS así. Podría funcionar en raras ocasiones donde la base de tiempo es la correcta, pero para el caso general no funcionará.

Debe cambiar esta situación:

pkt.pts = ix++; 
pkt.dts = pkt.pts; 

A esto:

pkt.pts = av_rescale_q(pkt.pts, ifcx->streams[0]->codec->time_base, ofcx->streams[0]->time_base); 
pkt.dts = av_rescale_q(pkt.dts, ifcx->streams[0]->codec->time_base, ofcx->streams[0]->time_base); 

Lo que hace es convertir el paquete de PTS/DTS desde las unidades utilizadas en el codec de la corriente de entrada a las unidades de la flujo de salida.

Además, algunas corrientes tienen múltiples garrapatas-por-marco, por lo que si el video funciona a doble velocidad que puede hacerle falta a este derecho por debajo de la línea anterior:

pkt.pts *= ifcx->streams[0]->codec->ticks_per_frame; 
pkt.dts *= ifcx->streams[0]->codec->ticks_per_frame; 
+0

Incluso con esto aplicado al código anterior, obtengo un archivo mp4 que tiene un tiempo aparentemente incorrecto. Aproximadamente 10 segundos de video grabado en un archivo mp4, cuando se reproduce, se completa al instante. Puedo pasar por todos los marcos, pero no se reproducirá. [mp4 @ 0x1040e9000] Duración prevista de la aplicación: 1967652701196434754/indicación de hora: 749796 está fuera de rango para el formato mov/mp4 [mp4 @ 0x1040e9000] pts no tiene ningún valor – stevex

1

En mi experiencia con una moderna H. 264 encoder, estoy descubriendo que la duración devuelta por ffmpeg es solo una "sugerencia" y que hay algo de "jitter" en el PTS. La única forma precisa de determinar la velocidad de cuadro o la duración es medirla usted mismo usando los valores PTS.

Para un codificador H.264 corriendo a 30 fps, la duración es siempre reportados como 3000/90000, mientras que la duración medida es usualmente +/- 1, pero periódicamente salta decir 3000 + 25 un fotograma y el siguiente 3000-25. Estoy suavizando esto para la grabación al notar cualquier cuadro adyacente con desviación opuesta y ajustando el PTS del segundo fotograma mientras se preserva la duración total.

Esto me da un flujo con una duración ocasional (calculada) de 30001 o 2999, que refleja la deriva del reloj.

Cuando se graba una secuencia de 29.97 fps, av_read_frame() siempre devuelve una duración de 3000, mientras que la duración nominal calculada es 3003 (correcta para 29.97) con la misma fluctuación y deriva que se describió anteriormente.

En mi caso, acabo de construir una máquina de estado para limpiar el tiempo. Esperando que esto ayude a alguien.