Search code examples
crtmpflvwowza

librtmp example c programming?


Hi guys im new to c programming and would like to know if anyone has a really simple example to open a .flv file and send to a wowza server using librtmp.

Ive found a lot of code that is able to connect using the library but not a lot on publishing an flv.

Basically:

init librtmp -> enable write -> connect -> packetize flv -> send packets until finish

EDIT:

Okay so i have to open my flv file first and isolate each packet in the flv file to send it to librtmp. So far ive found this code:

https://github.com/noirotm/flvmeta/blob/master/src/flv.c


Solution

  • This is the author of FLVMeta here.

    You might want to have a look at my answer to this question: https://stackoverflow.com/a/13803143/393701

    In addition to this information, I can explain some parts of a C++ software that I wrote (unfortunately not open-source for the moment) which purpose it to stream FLV files to an RTMP server.

    This has been tested and works fine with Wowza, nginx-rtmp, as well as the Dailymotion live streaming server.

    Initializing

    std::string rtmp_url = "rtmp://server/app/stream";
    // add this if doing live streaming:
    rtmp_url.append(" live=1");
    
    RTMP * rtmp = RTMP_Alloc();
    if (!rtmp) {
        std::cerr << "Unable to create rtmp object\n";
        return;
    }
    
    RTMP_Init(rtmp);
    RTMP_LogSetLevel(RTMP_LOGINFO);
    RTMP_LogSetOutput(stderr);
    RTMP_SetupURL(rtmp, const_cast<char*>(rtmp_url.c_str())); // librtmp is a mess
    RTMP_EnableWrite(rtmp);
    

    The weird const_cast is due to the fact that the librtmp has been oddly designed, and uses a non-const char * as URL, even though it might not be modified actually by the library.
    Also, the buffer will be used for as long as the RTMP object lives, so the string must not be freed before deleting the RTMP object.

    Connecting

    // connect to server
    if (!RTMP_Connect(rtmp, NULL)) {
        std::cerr << "Unable to connect to server\n";
        RTMP_Free(rtmp);
        return;
    }
    

    The second argument to RTMP_Connect is an optional pointer to RTMP packet to send as a parameter to the connection instead of the default connection packet sent by librtmp. This normally should not be used when connecting to 'normal' RTMP servers.

    // connect to stream (this will be the stream specified in the RTMP URL)
    if (!RTMP_ConnectStream(rtmp, 0)) {
        cerr << "Unable to connect to stream\n";
        RTMP_Free(rtmp);
        return;
    }
    

    The second argument to RTMP_ConnectStream is an integer representing the timestamp to seek to when playing a video. When publishing, using 0 is a safe bet.

    Publishing packets

    This section assumes the use of the FLV code found in the FLVMeta project (see https://github.com/noirotm/flvmeta/blob/master/src/flv.h#L175).

    flv_stream * flvin = flv_open(input_file.c_str());
    if (!flvin) {
        std::cerr << "Unable to open " << input_file << '\n';
        return;
    }
    

    First, it is a good idea to make sure we have opened a real FLV file.

    flv_header header;
    int res = flv_read_header(flvin, &header);
    if (res == FLV_ERROR_NO_FLV || res == FLV_ERROR_EOF) {
        std::cerr << "Input file is not an FLV video\n";
        flv_close(flvin);
        return;
    }
    

    Now read every tag in the file, and send them to the RTMP server.

    // 10 MB copy buffer should be enough
    #define BUFFER_SIZE 10000000
    char buffer[BUFFER_SIZE];
    
    flv_tag tag;
    while (flv_read_tag(flvin, &tag) != FLV_ERROR_EOF) {
        // copy tag header
        flv_copy_tag(buffer, &tag, FLVSTREAMER_BUFFER_SIZE);
    
        // copy tag body
        size_t data_size = flv_read_tag_body(flvin, buffer + FLV_TAG_SIZE, BUFFER_SIZE - FLV_TAG_SIZE);
    
        // copy previous tag size
        uint32 pts;
        flv_read_prev_tag_size(flvin, &pts);
        flv_copy_prev_tag_size(
            buffer + FLV_TAG_SIZE + flv_tag_get_body_length(tag),
            pts,
            BUFFER_SIZE - (FLV_TAG_SIZE + flv_tag_get_body_length(tag))
        );
    
        // write the packet
        int size = FLV_TAG_SIZE + data_size + sizeof(uint32);
        if (RTMP_Write(rtmp, buffer, size) <= 0) {
            std::cerr << "Unable to write to server" << endl;
            break;
        }
    }
    

    The various flv_* functions take care of handling the specificities of the FLV format, for example endianness issues, and the eventual malforming of data, so I would recommend using them if your program is compatible with the GPL license.
    If not, it is not complicated to use standard I/O to iterate through tags, as long as the FLV format is well understood.

    It is also important to note that the FLV file header must not be sent. librtmp will detect any attempt to send the FLV header and prevent it.

    It is not mandatory to read incoming RTMP packets while publishing, but if you want to properly handle the protocol, the approach used by @szatmary in his code is recommended.

    Another important aspect that is not handled here is that when publishing a live stream, it is mandatory to implement some form of throttling in order to send data at the nominal bitrate of the video, otherwise the RTMP server will skip packets and video playback will suffer greatly.
    A common approach to solve this problem could be to read each tag's timestamp, and sleep if we are writing too fast.

    Cleaning up

    flv_close(flvin);
    RTMP_Free(rtmp);
    

    The RTMP_Free function will take care of closing the stream, the connection, and free any allocated memory.