2012-05-23 65 views
21

Tôi có IPCamera trên video phát trực tuyến LAN của mình bằng RTSP. Tôi đã có thể nắm bắt và hiển thị nó thành công sử dụng lệnh ffplay:Nhận luồng RTSP bằng thư viện FFMPEG

ffplay rtsp://admin:[email protected]:7070 

(với xác thực)

Vì vậy, tôi muốn để đạt được các lập trình sử dụng tương tự trong C/C++ sử dụng ffmpeg thư viện. Tôi đoán điều này là có thể.

Vì vậy, hãy để tôi cụm từ hai câu hỏi đơn giản:

  1. Làm thế nào để tôi nhận được dòng trong một chương trình C/C++ sử dụng thư viện FFMPEG? (chỉ cần cung cấp một số URL/hướng dẫn, vì google không hữu ích)

  2. Làm cách nào để hiển thị video đã nhận? (cùng ở đây, một số URL tốt để hướng dẫn tôi).

Trả lời

28

Đối rtsp suối sau đang làm việc cho tôi (sau khi nhận được khung i lưu kết quả vào một tập tin ppm):

#include <stdio.h> 
#include <stdlib.h> 
#include <iostream> 
#include <fstream> 
#include <sstream> 

extern "C" 
{ 
    #include <avcodec.h> 
    #include <avformat.h> 
    #include <avio.h> 
    #include <swscale.h> 
} 

void log_callback(void *ptr, int level, const char *fmt, va_list vargs) 
{ 
    static char message[8192]; 
    const char *module = NULL; 

    if (ptr) 
    { 
     AVClass *avc = *(AVClass**) ptr; 
     module = avc->item_name(ptr); 
    } 
    vsnprintf_s(message, sizeof(message), fmt, vargs); 

    std::cout << "LOG: " << message << std::endl; 
} 


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

    SwsContext *img_convert_ctx; 
    AVFormatContext* context = avformat_alloc_context(); 
    AVCodecContext* ccontext = avcodec_alloc_context(); 
    int video_stream_index; 

    av_register_all(); 
    avformat_network_init(); 
    //av_log_set_callback(&log_callback); 

    //open rtsp 
    if(avformat_open_input(&context, "rtsp://134.169.178.187:8554/h264.3gp",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.mp4",NULL); 
    AVFormatContext* oc = avformat_alloc_context(); 
    //oc->oformat = fmt; 
    //avio_open2(&oc->pb, "test.mp4", 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 

    AVCodec *codec = NULL; 
    codec = avcodec_find_decoder(CODEC_ID_H264); 
    if (!codec) exit(1); 

    avcodec_get_context_defaults3(ccontext, codec); 
    avcodec_copy_context(ccontext,context->streams[video_stream_index]->codec); 
    std::ofstream myfile; 

    if (avcodec_open(ccontext, codec) < 0) exit(1); 

    img_convert_ctx = sws_getContext(ccontext->width, ccontext->height, ccontext->pix_fmt, ccontext->width, ccontext->height, 
          PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL); 

    int size = avpicture_get_size(PIX_FMT_YUV420P, ccontext->width, ccontext->height); 
    uint8_t* picture_buf = (uint8_t*)(av_malloc(size)); 
    AVFrame* pic = avcodec_alloc_frame(); 
    AVFrame* picrgb = avcodec_alloc_frame(); 
    int size2 = avpicture_get_size(PIX_FMT_RGB24, ccontext->width, ccontext->height); 
    uint8_t* picture_buf2 = (uint8_t*)(av_malloc(size2)); 
    avpicture_fill((AVPicture *) pic, picture_buf, PIX_FMT_YUV420P, ccontext->width, ccontext->height); 
    avpicture_fill((AVPicture *) picrgb, picture_buf2, PIX_FMT_RGB24, ccontext->width, ccontext->height); 

    while(av_read_frame(context,&packet)>=0 && cnt <1000) 
    {//read 100 frames 

     std::cout << "1 Frame: " << cnt << std::endl; 
     if(packet.stream_index == video_stream_index){//packet is video 
      std::cout << "2 Is Video" << std::endl; 
      if(stream == NULL) 
      {//create stream in file 
       std::cout << "3 create stream" << std::endl; 
       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; 
      } 
      int check = 0; 
      packet.stream_index = stream->id; 
      std::cout << "4 decoding" << std::endl; 
      int result = avcodec_decode_video2(ccontext, pic, &check, &packet); 
      std::cout << "Bytes decoded " << result << " check " << check << std::endl; 
      if(cnt > 100)//cnt < 0) 
      { 
       sws_scale(img_convert_ctx, pic->data, pic->linesize, 0, ccontext->height, picrgb->data, picrgb->linesize); 
       std::stringstream name; 
       name << "test" << cnt << ".ppm"; 
       myfile.open(name.str()); 
       myfile << "P3 " << ccontext->width << " " << ccontext->height << " 255\n"; 
       for(int y = 0; y < ccontext->height; y++) 
       { 
        for(int x = 0; x < ccontext->width * 3; x++) 
         myfile << (int)(picrgb->data[0] + y * picrgb->linesize[0])[x] << " "; 
       } 
       myfile.close(); 
      } 
      cnt++; 
     } 
     av_free_packet(&packet); 
     av_init_packet(&packet); 
    } 
    av_free(pic); 
    av_free(picrgb); 
    av_free(picture_buf); 
    av_free(picture_buf2); 

    av_read_pause(context); 
    avio_close(oc->pb); 
    avformat_free_context(oc); 

    return (EXIT_SUCCESS); 
} 
+0

cảm ơn câu trả lời. Tôi đã cài đặt ffmpeg và x264 theo các hướng dẫn [this] (http://ffmpeg.org/trac/ffmpeg/wiki/UbuntuCompilationGuide). Cài đặt thành công và các thư viện được cài đặt trong/usr/lib của hệ thống ubuntu của tôi. Và khi tôi đang cố gắng để biên dịch mã ur (từ /home/bhanu/main.cpp) bằng cách sử dụng Nhưng tôi đang đối mặt với một số vấn đề với cờ thích hợp tôi tiếp tục nhận được lỗi liên kết. Tôi sẽ gửi cho họ, xin vui lòng hướng dẫn tôi những gì/nơi có thể sai lầm được? –

+0

Lệnh tôi đang sử dụng là 'g ++ main.cpp -lavcodec -lavdevice -lavfilter -lavformat -lavutil -logg -lrtmp -lswscale -lx264 -lpthread -lvorbis -L/usr/lib', tôi nhận được kết quả sau 'chính .cpp: Trong hàm 'int main (int, char **)': /home/bhanu/work_environment/softwares/ffmpeg/libavformat/allformats.c:49: không xác định tham chiếu đến 'avcodec_register_all' /usr/lib/libavformat .a (oggparsevorbis.o): Trong hàm 'vorbis_packet': collect2: ld trả về 1 trạng thái thoát ' và một vài lỗi tương tự. Vui lòng tham khảo liên kết [ở đây] (http://pastebin.com/B8r3qcti) để có kết quả đầu ra trình biên dịch hoàn chỉnh. –

+0

'cnt <1000) {// đọc 100 khung hình' có vẻ như mã đọc 1000 khung hình ... – Pimgd

1

kiểm tra tập tin ./configure của bạn. Có thể biên dịch cho armlinux không cho số x86. Đó là lý do bạn đang nhận được tài liệu tham khảo không xác định để 'avcodec_register_all'

Dưới đây là giải pháp: -

$ cd ffmpeg 

$export LD_RUN_PATH=/usr/local/lib:/usr/lib:/lib 

$ ./configure 

$ make && make install 

Và sau đó biên dịch ứng dụng của bạn.

4

FFmpeg có thể mở luồng rtsp trực tiếp giống như mở tệp video cục bộ. Here là mã hướng dẫn cập nhật phiên bản mới nhất của FFmpeg.

6

FWIW, tôi đã cập nhật mã do @technique cung cấp để làm việc với các thư viện tôi có từ FFMPEG 3.2.2. Hy vọng rằng điều này sẽ giúp một người nào đó Googling này. Có một số thay đổi nhỏ có thể gây nhầm lẫn cho những người ngại khi trả lời câu hỏi này.

#include <stdio.h> 
#include <stdlib.h> 
#include <iostream> 
#include <fstream> 
#include <sstream> 

extern "C" { 
#include <libavcodec/avcodec.h> 
#include <libavformat/avformat.h> 
#include <libavformat/avio.h> 
#include <libswscale/swscale.h> 
} 

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

    // Open the initial context variables that are needed 
    SwsContext *img_convert_ctx; 
    AVFormatContext* format_ctx = avformat_alloc_context(); 
    AVCodecContext* codec_ctx = NULL; 
    int video_stream_index; 

    // Register everything 
    av_register_all(); 
    avformat_network_init(); 

    //open RTSP 
    if (avformat_open_input(&format_ctx, "rtsp://134.169.178.187:8554/h264.3gp", 
      NULL, NULL) != 0) { 
     return EXIT_FAILURE; 
    } 

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

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

    AVPacket packet; 
    av_init_packet(&packet); 

    //open output file 
    AVFormatContext* output_ctx = avformat_alloc_context(); 

    AVStream* stream = NULL; 
    int cnt = 0; 

    //start reading packets from stream and write them to file 
    av_read_play(format_ctx); //play RTSP 

    // Get the codec 
    AVCodec *codec = NULL; 
    codec = avcodec_find_decoder(AV_CODEC_ID_H264); 
    if (!codec) { 
     exit(1); 
    } 

    // Add this to allocate the context by codec 
    codec_ctx = avcodec_alloc_context3(codec); 

    avcodec_get_context_defaults3(codec_ctx, codec); 
    avcodec_copy_context(codec_ctx, format_ctx->streams[video_stream_index]->codec); 
    std::ofstream output_file; 

    if (avcodec_open2(codec_ctx, codec, NULL) < 0) 
     exit(1); 

    img_convert_ctx = sws_getContext(codec_ctx->width, codec_ctx->height, 
      codec_ctx->pix_fmt, codec_ctx->width, codec_ctx->height, AV_PIX_FMT_RGB24, 
      SWS_BICUBIC, NULL, NULL, NULL); 

    int size = avpicture_get_size(AV_PIX_FMT_YUV420P, codec_ctx->width, 
      codec_ctx->height); 
    uint8_t* picture_buffer = (uint8_t*) (av_malloc(size)); 
    AVFrame* picture = av_frame_alloc(); 
    AVFrame* picture_rgb = av_frame_alloc(); 
    int size2 = avpicture_get_size(AV_PIX_FMT_RGB24, codec_ctx->width, 
      codec_ctx->height); 
    uint8_t* picture_buffer_2 = (uint8_t*) (av_malloc(size2)); 
    avpicture_fill((AVPicture *) picture, picture_buffer, AV_PIX_FMT_YUV420P, 
      codec_ctx->width, codec_ctx->height); 
    avpicture_fill((AVPicture *) picture_rgb, picture_buffer_2, AV_PIX_FMT_RGB24, 
      codec_ctx->width, codec_ctx->height); 

    while (av_read_frame(format_ctx, &packet) >= 0 && cnt < 1000) { //read ~ 1000 frames 

     std::cout << "1 Frame: " << cnt << std::endl; 
     if (packet.stream_index == video_stream_index) { //packet is video 
      std::cout << "2 Is Video" << std::endl; 
      if (stream == NULL) { //create stream in file 
       std::cout << "3 create stream" << std::endl; 
       stream = avformat_new_stream(output_ctx, 
         format_ctx->streams[video_stream_index]->codec->codec); 
       avcodec_copy_context(stream->codec, 
         format_ctx->streams[video_stream_index]->codec); 
       stream->sample_aspect_ratio = 
         format_ctx->streams[video_stream_index]->codec->sample_aspect_ratio; 
      } 
      int check = 0; 
      packet.stream_index = stream->id; 
      std::cout << "4 decoding" << std::endl; 
      int result = avcodec_decode_video2(codec_ctx, picture, &check, &packet); 
      std::cout << "Bytes decoded " << result << " check " << check 
        << std::endl; 
      if (cnt > 100) //cnt < 0) 
        { 
       sws_scale(img_convert_ctx, picture->data, picture->linesize, 0, 
         codec_ctx->height, picture_rgb->data, picture_rgb->linesize); 
       std::stringstream file_name; 
       file_name << "test" << cnt << ".ppm"; 
       output_file.open(file_name.str().c_str()); 
       output_file << "P3 " << codec_ctx->width << " " << codec_ctx->height 
         << " 255\n"; 
       for (int y = 0; y < codec_ctx->height; y++) { 
        for (int x = 0; x < codec_ctx->width * 3; x++) 
         output_file 
           << (int) (picture_rgb->data[0] 
             + y * picture_rgb->linesize[0])[x] << " "; 
       } 
       output_file.close(); 
      } 
      cnt++; 
     } 
     av_free_packet(&packet); 
     av_init_packet(&packet); 
    } 
    av_free(picture); 
    av_free(picture_rgb); 
    av_free(picture_buffer); 
    av_free(picture_buffer_2); 

    av_read_pause(format_ctx); 
    avio_close(output_ctx->pb); 
    avformat_free_context(output_ctx); 

    return (EXIT_SUCCESS); 
} 

Nó có thể được biên soạn bởi một cái gì đó để tác động của:

g++ -w my_streamer.cpp -o my_streamer $(pkg-config --cflags --libs libavformat libswscale) 

tôi bao gồm các -w vì có rất nhiều cảnh báo deprecation cho các chức năng này tại thư viện. Bạn sẽ cần phải có pkg-config cũng như các thư viện libavformat và libswscale được cài đặt.

+0

kể từ khi cập nhật của bạn một vài điều đã thay đổi. Vui lòng xem xét thêm phiên bản mới. Cảm ơn :-) –

Các vấn đề liên quan