简体   繁体   中英

Streaming H.264 over UDP using FFmpeg, and "dimensions not set" error

I'm trying to stream H.264 over UDP with no luck so far. Here is a minimal code that you can reproduce the problem.

To compile,

g++ -o test -lavcodec -lavformat -lavutil test.cpp

Extra information, I start ffplay as follows. Currently it's of no use.

ffplay -i udp://127.0.0.1:8554/live.sdp

Output of my code (see avio_open() call),

[libx264 @ 0x6a26c0] using mv_range_thread = 24
[libx264 @ 0x6a26c0] using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.1 Cache64
[libx264 @ 0x6a26c0] profile High, level 3.1
Output #0, h264, to 'udp://127.0.0.1:8554/live.sdp':
    Stream #0:0, 0, 0/0: Video: h264 (libx264), -1 reference frame, none, q=-1--1
[h264 @ 0x6a2020] dimensions not set
Cannot write header to stream: Success

And the code,

extern "C" {
    #include <libavcodec/avcodec.h>
    #include <libavformat/avformat.h>
    #include <libavutil/avutil.h>
}

#include <iostream>
using namespace std;

int main() {
    AVCodecContext* m_codecContext;
    AVCodec* m_codec;
    AVFormatContext* m_formatContext;
    AVStream* m_stream;

    unsigned m_outWidth = 768;
    unsigned m_outHeight = 608;

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

    int errorStatus = 0;
    char errorLog[128] = { 0 };
    av_log_set_level(AV_LOG_TRACE);

    string m_output("udp://127.0.0.1:8554/live.sdp");

    if (avformat_alloc_output_context2(&m_formatContext, NULL, "h264", m_output.c_str()) < 0) {
        cerr << "Cannot allocate output context: "
             << av_make_error_string(errorLog, 128, errorStatus) << endl;
        return -1;
    }

    AVOutputFormat *m_outputFormat = m_formatContext->oformat;

    m_codec = avcodec_find_encoder(AV_CODEC_ID_H264);
    if (!m_codec) {
        cerr << "Cannot find an encoder: "
             << av_make_error_string(errorLog, 128, errorStatus) << endl;
        return -1;
    }

    m_codecContext = avcodec_alloc_context3(m_codec);
    if (!m_codecContext) {
        cerr << "Cannot allocate a codec context: "
             << av_make_error_string(errorLog, 128, errorStatus) << endl;
        return -1;
    }

    m_codecContext->pix_fmt = AV_PIX_FMT_YUV420P;
    m_codecContext->width = m_outWidth;
    m_codecContext->height = m_outHeight;

    if (avcodec_open2(m_codecContext, m_codec, NULL) < 0) {
        cerr << "Cannot open codec: "
             << av_make_error_string(errorLog, 128, errorStatus) << endl;
        return -1;
    }

    m_stream = avformat_new_stream(m_formatContext, m_codec);
    if (!m_stream) {
        cerr << "Cannot create a new stream: "
             << av_make_error_string(errorLog, 128, errorStatus) << endl;
        return -1;
    }

    av_dump_format(m_formatContext, 0, m_output.c_str(), 1);

    if ((errorStatus = avio_open(&m_formatContext->pb, m_output.c_str(), AVIO_FLAG_WRITE)) < 0) {
        cerr << "Cannot open output: "
             << av_make_error_string(errorLog, 128, errorStatus) << endl;
        return -1;
    }

    if (avformat_write_header(m_formatContext, NULL) < 0) {
        cerr << "Cannot write header to stream: "
             << av_make_error_string(errorLog, 128, errorStatus) << endl;
        return -1;
    }

    cout << "All done." << endl;

    return 0;
}

For those who has even more time to spare on my problem, when I change m_output to rtsp://127.0.0.1:8554/live.sdp , and ffplay command to ffplay -rtsp_flags listen -i rtsp://127.0.0.1:8554/live.sdp I get the error,

[libx264 @ 0x1e056c0] using mv_range_thread = 24
[libx264 @ 0x1e056c0] using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.1 Cache64
[libx264 @ 0x1e056c0] profile High, level 3.1
Output #0, h264, to 'rtsp://127.0.0.1:8554/live.sdp':
    Stream #0:0, 0, 0/0: Video: h264 (libx264), -1 reference frame, none, q=-1--1
Cannot open output: Protocol not found

Am I naive to expect that streaming protocol will be changed like this?

Apparently I was creating and initializing an AVCodecContext and then not using it.. An AVStream has its own AVCodecContext (which is quite sensible, as different streams usually need different encoders) and it was the one that I needed to initialize with dimensions and pixel format. For that, I also needed to swap the calls to avformat_new_stream() and avcodec_open2() and call the former first. Here is the diff of the fix.

Note that m_codecContext is removed altogether.

--- rtsp-so.cpp.orig    2015-09-03 17:34:08.190375415 +0200
+++ rtsp-so.cpp 2015-09-03 17:43:42.166542704 +0200
@@ -8,7 +8,6 @@
 using namespace std;

 int main() {
-    AVCodecContext* m_codecContext;
     AVCodec* m_codec;
     AVFormatContext* m_formatContext;
     AVStream* m_stream;
@@ -41,32 +40,25 @@
         return -1;
     }

-    m_codecContext = avcodec_alloc_context3(m_codec);
-    if (!m_codecContext) {
-        cerr << "Cannot allocate a codec context: "
+    m_stream = avformat_new_stream(m_formatContext, m_codec);
+    if (!m_stream) {
+        cerr << "Cannot create a new stream: "
              << av_make_error_string(errorLog, 128, errorStatus) << endl;
         return -1;
     }

-    m_codecContext->pix_fmt = AV_PIX_FMT_YUV420P;
-    m_codecContext->width = m_outWidth;
-    m_codecContext->height = m_outHeight;
+    av_dump_format(m_formatContext, 0, m_output.c_str(), 1);

-    if (avcodec_open2(m_codecContext, m_codec, NULL) < 0) {
-        cerr << "Cannot open codec: "
-             << av_make_error_string(errorLog, 128, errorStatus) << endl;
-        return -1;
-    }
+    m_stream->codec->pix_fmt = AV_PIX_FMT_YUV420P;
+    m_stream->codec->width = m_outWidth;
+    m_stream->codec->height = m_outHeight;

-    m_stream = avformat_new_stream(m_formatContext, m_codec);
-    if (!m_stream) {
-        cerr << "Cannot create a new stream: "
+    if (avcodec_open2(m_stream->codec, m_codec, NULL) < 0) {
+        cerr << "Cannot open codec: "
              << av_make_error_string(errorLog, 128, errorStatus) << endl;
         return -1;
     }

-    av_dump_format(m_formatContext, 0, m_output.c_str(), 1);
-
     if ((errorStatus = avio_open(&m_formatContext->pb, m_output.c_str(), AVIO_FLAG_WRITE)) < 0) {
         cerr << "Cannot open output: "
              << av_make_error_string(errorLog, 128, errorStatus) << endl;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM