Empfangen des RTSP-Streams mit der FFMPEG-Bibliothek

Lesezeit: 11 Minuten

Empfangen des RTSP Streams mit der FFMPEG Bibliothek
Bhanu Kiran

Ich habe eine IPCamera in meinem LAN, die Videos mit RTSP streamt. Ich konnte es mit dem Befehl ffplay erfolgreich erfassen und anzeigen:

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

(mit Authentifizierung)

Daher möchte ich dasselbe mit der Programmierung in C/C++ erreichen ffmpeg Bücherei. Ich denke, das muss möglich sein.

Lassen Sie mich also zwei einfache Fragen formulieren:

  1. Wie erhalte ich den Stream in einem C/C++-Programm mit der FFMPEG-Bibliothek? (Geben Sie einfach eine URL/ein Tutorial an, da Google nicht hilfreich war)

  2. Wie zeige ich das empfangene Video an? (dasselbe hier, eine gute URL, um mich zu leiten).

Für rtsp-Streams funktioniert bei mir Folgendes (nach dem Empfangen von Frames speichere ich das Ergebnis in einer ppm-Datei):

    #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 << " 255n";
                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);
}

  • danke für die Antwort. Ich habe ffmpeg und x264 im Anschluss installiert diese Anweisungen. Die Installation ist erfolgreich und die Bibliotheken werden in /usr/lib meines Ubuntu-Systems installiert. Und wenn ich versuche, Ihren Code (von /home/bhanu/main.cpp) mit Aber ich habe einige Probleme mit entsprechenden Flags zu kompilieren, erhalte ich immer wieder Linker-Fehler. Ich werde sie posten, bitte führen Sie mich, was/wo der Fehler sein könnte?

    – Bhanu Kiran

    13. Juli 12 um 14:03 Uhr


  • Der Befehl, den ich verwende, ist g++ main.cpp -lavcodec -lavdevice -lavfilter -lavformat -lavutil -logg -lrtmp -lswscale -lx264 -lpthread -lvorbis -L/usr/lib, erhalte ich die folgende Ausgabe main.cpp: In function 'int main(int, char**)': /home/bhanu/work_environment/softwares/ffmpeg/libavformat/allformats.c:49: undefined reference to 'avcodec_register_all' /usr/lib/libavformat.a(oggparsevorbis.o): In function 'vorbis_packet': collect2: ld returned 1 exit status und wenige ähnliche Fehler. Bitte Link beachten Hier für die vollständige Compilerausgabe.

    – Bhanu Kiran

    13. Juli 12 um 14:17 Uhr


  • cnt <1000) {//read 100 frames es sieht so aus, als würde der Code stattdessen 1000 Frames lesen …

    – Pimgd

    19. August 14 um 14:59 Uhr

  • Ich habe einen Kern für die Fehlerprotokolle erstellt, die während der Dekodierung des RTSP-Streams mit FFMPEG generiert werden. gist.github.com/anonymous/64b3df25b7f103c0da54 Irgendeine Idee, wie man die obigen Fehlerprotokolle löst. Das angezeigte Bild ist verschwommen.

    – Tarik

    27. Juni 15 um 11:46 Uhr

  • @technique Das war hilfreich für mich und ich habe eine aktualisierte Version als neue Antwort hinzugefügt, die mit Bibliotheken mit ffmpeg 3.2.2 funktioniert, falls jemand dies über Google findet. Sie haben einige der Funktionen und Aufzählungen geändert, die mich zuerst für eine Schleife warfen.

    – synthetisch

    11. Januar 17 um 18:13 Uhr

Empfangen des RTSP Streams mit der FFMPEG Bibliothek
synthetisch

FWIW, ich habe den von @technique bereitgestellten Code aktualisiert, um mit den Bibliotheken zu arbeiten, die ich von FFMPEG 3.2.2 habe. Hoffentlich hilft das jemandem, der das googelt. Es gibt einige kleine Änderungen, die für Leute, die über diese Antwort stolpern, verwirrend sein könnten.

#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
                        << " 255n";
                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);
}

Es kann durch etwas kompiliert werden mit dem Effekt von:

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

Ich schließe die ein -w da es viele Verfallswarnungen für diese Funktionen in der Bibliothek gibt. Sie müssen pkg-config sowie die Bibliotheken libavcodec, libavformat, libswscale und libavutil installiert haben. (h/t @BobCheng für fehlende Bibliotheken).

  • Seit deinem Update hat sich einiges geändert. Bitte erwägen Sie, eine neue Version hinzuzufügen. Danke 🙂

    – Folkert van Heusden

    10. August 17 um 11:22 Uhr

  • @FolkertvanHeusden Ich arbeite nur mit 3.2.2 (und das musste ich schon lange nicht mehr), also bin ich leider nicht auf dem Laufenden über neue Funktionen oder Änderungen 🙁

    – synthetisch

    9. Mai 18 um 14:26 Uhr

  • Kleine Änderung: Sie benötigen libavcodec & libavutil, um erfolgreich zu kompilieren: g++ -w my_streamer.cpp -o my_streamer $(pkg-config –cflags –libs libavcodec libavformat libswscale libavutil)

    – Bob Cheng

    6. Januar 21 um 8:17 Uhr

  • Schätzen Sie es @BobCheng – ich bin mir nicht sicher, ob mein Setup es mir erlaubt hat, diese aus irgendeinem Grund nicht einzuschließen, aber trotzdem funktioniert. Ich habe es der Antwort für alle, die es kopieren/einfügen, hinzugefügt

    – synthetisch

    7. Januar 21 um 18:10 Uhr


Empfangen des RTSP Streams mit der FFMPEG Bibliothek
Qin Peixi

FFmpeg kann rtsp-Streams direkt öffnen, genau wie das Öffnen lokaler Videodateien. Hier ist ein Tutorial-Code, der mit der neuesten Version von FFmpeg aktualisiert wird.

1642945806 169 Empfangen des RTSP Streams mit der FFMPEG Bibliothek
Prashant

Überprüfen Sie Ihre ./configure-Datei. Kann sein, dass es kompiliert wird armlinux nicht für die x86. Deshalb erhalten Sie einen undefinierten Verweis auf 'avcodec_register_all'

Hier ist die Lösung: –

$ cd ffmpeg 

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

$ ./configure

$ make && make install

Und danach stellen Sie Ihre Bewerbung zusammen.

.

604790cookie-checkEmpfangen des RTSP-Streams mit der FFMPEG-Bibliothek

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy