[libav-bugs] [Bug 892] New: Metadata stream of MPEG-2 file not being written out correctly.

bugzilla at libav.org bugzilla at libav.org
Tue Sep 15 12:44:40 CEST 2015


https://bugzilla.libav.org/show_bug.cgi?id=892

            Bug ID: 892
           Summary: Metadata stream of MPEG-2 file not being written out
                    correctly.
           Product: Libav
           Version: 11
          Hardware: Other
                OS: Linux
            Status: NEW
          Severity: normal
          Priority: ---
         Component: libavformat
          Assignee: bugzilla at libav.org
          Reporter: anthony.venables at eads.com

I am using the LIBAV libraries in a C++ application I am writing. The
application should open a MPEG-2 file for display and save a tagged section to
a new output file.

The input MPEG-2 file consisting of the following three streams.

a) a synchronous metadata stream of KLV data.
b) a video stream.
c) an audio stream.

The file is opened and the packets are read one at a time and decoded for
display to the user. I need to be able to copy some (or all) of the input file
to an output file. I have written a function that performs an av_seek_frame()
to get to the first frame to output, then creates an output file. The input
file (which is already open) is read one packet at a time and writtsn to the
output file.

Here is the code segment that opens the file for reading. The code is run
during construction of the class that opens and reads the MPEG2 file.

    {

        pFormatCtx = NULL;
        pCodecCtx = NULL;
        pCodec = NULL;
        pFrame = NULL;
        packet = NULL;
        buffer = NULL;
        img_convert_ctx = NULL;
        memset(&next_time, 0, sizeof(next_time));
        current_index = 0;
        for (unsigned int i = 0; i < MAX_INPUT_BUFFERS; ++i)

            pFrameYUYV[i] = NULL;

        setFormat(Frame::YUYV);
        initExternalBuffer = false;

        if (!initialized)
        {

            return;

        }

        if ( pFormatCtx != NULL )
        {

            delete pFormatCtx;
            pFormatCtx = NULL;
            av_free(packet);
            packet = NULL;

        }
        if (filename_ == NULL)
        {

            VIDEOLOG_WARNING("Invalid NULL file name.");
            initialized = false;
            return;

        }

        packet =(AVPacket *)av_malloc(sizeof(AVPacket));
        if (packet == NULL)
        {

            VIDEOLOG_WARNING("Could not allocate packet variable.");
            initialized = false;
            return;

        }

        Open video file
        if(avformat_open_input(&pFormatCtx, const_cast <const char *>
(filename_), NULL, NULL)!=0)
        {

            VIDEOLOG_WARNING("Could not open file.");
            initialized = false;
            return;

        }

        Retrieve stream information
        if(avformat_find_stream_info(pFormatCtx, NULL) < 0)
        {

            VIDEOLOG_WARNING("Could not find stream information.");
            initialized = false;
            return;

        }

        Dump information about file onto standard error
        av_dump_format(pFormatCtx, 0, const_cast <const char *> (filename_),
false);

    }


Here is the function I use to output to a file.

    void FFMpegInput::save_clip_to_file(

        const std::string& filename )

    {

        Get start and end time values. These are relative positions within the
clip in microseconds.
        A value of 0 is start of clip.
        uint64_t start_marker_position =
EventListContainer::get_instance().get_start_marker_position();
        uint64_t end_marker_position =
EventListContainer::get_instance().get_end_marker_position();

        convert start and end targets to fractional seconds
        uint64_t start_target =

            convert_microseconds_to_fractional_seconds(

                start_marker_position,
                m_current_video_stream );

        uint64_t end_target =

            convert_microseconds_to_fractional_seconds(

                end_marker_position,
                m_current_video_stream );

        add video start pts to get actual target values.
        start_target += m_video_start_pts;
        end_target += m_video_start_pts;

        Seek to start target position.
        unsigned int seek_stream_id = m_current_video_stream;

        if ( m_current_uas_data_stream >= 0 )
        {

            seek on data stream if possible because data
            stream leads video stream by a small amount.
            seek_stream_id = m_current_uas_data_stream;

cout << "seeking on data_stream" << endl;

    }

    av_seek_frame(

        pFormatCtx,
        seek_stream_id,
        start_target,
        AVSEEK_FLAG_ANY );

    avcodec_flush_buffers(pCodecCtx);

    open file
    AVOutputFormat *fmt;
    AVFormatContext *oc;

    fmt = av_guess_format(NULL, filename.c_str(), NULL);

    /* allocate the output media context */
    oc = avformat_alloc_context();
    if (!oc)
    {

        fprintf(stderr, "Memory error\n");
        exit(1);

    }

    oc->oformat = fmt;

    /* open the output file, if needed */
    if (!(fmt->flags & AVFMT_NOFILE))
    {

        if ( avio_open(&oc->pb, filename.c_str(), AVIO_FLAG_WRITE) < 0 )
        {

            fprintf(stderr, "Could not open '%s'\n", filename.c_str());
            exit(1);

        }

    }

    add streams to output file
    AVStream *st;
    AVStream *data_st;
    bool event_stream_present = false;

    for ( unsigned int i = 0;

        i < pFormatCtx->nb_streams;
        ++i )

    {

        create new stream and copy details from input file streams
        st = avformat_new_stream(oc, NULL );
        *st = *pFormatCtx->streams[i];

    }

    dump format of output context to screen
    av_dump_format(oc, 0, filename.c_str(), true);

    write the stream header, if any
    cout << "write the stream header ..." << endl;

    /*

    * @param s Media file handle, must be allocated with
avformat_alloc_context().
    * Its oformat field must be set to the desired output format;
    * Its pb field must be set to an already opened AVIOContext. */
avformat_write_header( oc, NULL ); cout << "write the stream header - complete
..." << endl; 

        uint64_t counter = start_target;

        cout << "start target = " << start_target << " end target = " <<
end_target << endl;

        loop from start marker to end marker
        while ( counter < end_target )
        {

            get next frame
            if (av_read_frame(pFormatCtx, packet) >= 0 )
            {

                if ( packet->stream_index==m_current_video_stream )
                {

                    m_video_current_pts = packet->pts;

                    if ( m_video_start_pts == 0 )
                    {

                        m_video_start_pts = m_video_current_pts;

                    }

                    counter = m_video_current_pts;

                }

                write frame
                int errnum = av_write_frame(oc, packet);
                if ( errnum != 0 )
                {

                    something went wrong - report it.
                    cout << "failed packet write, errnum = " << errnum << endl;

                }

                av_free_packet( packet );

            }
            else
            {

                cout << "failed to read frame."

                    << " counter = " << counter
                    << " end_target = " << end_target << endl;

                counter = end_target;

            }

        }

        close file
        write the trailer, if any. the trailer must be written
        before you close the CodecContexts? open when you wrote the
        header; otherwise write_trailer may try to use memory that
        was freed on av_codec_close()

        cout << "write the stream trailer ..." << endl;
        av_write_trailer(oc);
        cout << "write the stream trailer - complete ..." << endl;

        if (!(fmt->flags & AVFMT_NOFILE))
        {

            close the output file
            avio_close( oc->pb );

        }

    }

At run time the following output was generated by the 'av_dump_format' function
and by the cout calls.

Input #0, mpegts, from '/mercury/VIDEO_FILES/AAA_TEST_FILE_INPUT.ts':
  Duration: 00:04:33.77, start: 34009.985467, bitrate: 1855 kb/s
  Program 1 
    Stream #0.0[0xfc]: Data: [21][0][0][0] / 0x0015
    Stream #0.1[0xe0]: Video: h264 (Main), yuv420p, 1280x720, 50 fps, 90k tbn,
100 tbc
    Stream #0.2[0xc0]: Audio: mp2, 44100 Hz, 2 channels, s16p, 128 kb/s

Output #0, mpegts, to
'/mercury/VIDEO_FILES/AAA_TEST_FILE_OUTPUT_libav_11.4.ts':
    Stream #0.0: Data: [21][0][0][0] / 0x0015
    Stream #0.1: Video: h264 (Main), yuv420p, 1280x720, q=2-31, 50 fps, 90k
tbn, 100 tbc
    Stream #0.2: Audio: mp2, 44100 Hz, 2 channels, s16p, 128 kb/s

write the stream header ...
write the stream header - complete ...
start target = 3082570312 end target = 3085525923
write the stream trailer ...
write the stream trailer - complete ...

When the saved file is then loaded, the following output is generated by the
'av_dump_format' function call directly after opening the file.

Input #0, mpegts, from
'/mercury/VIDEO_FILES/AAA_TEST_FILE_OUTPUT_libav_11.4.ts':
  Duration: 00:00:32.82, start: 34250.833367, bitrate: 16152 kb/s
  Program 1 
    Metadata:
      service_name    : Service01
      service_provider: Libav
    Stream #0.0[0xfc]: Data: [6][0][0][0] / 0x0006
    Stream #0.1[0xe0]: Video: h264 (Main), yuv420p, 1280x720, 50 fps, 90k tbn,
100 tbc
    Stream #0.2[0xc0]: Audio: mp2, 44100 Hz, 2 channels, s16p, 128 kb/s


Playback of this file within my application seemed to function correctly, but
when I investigated the output file in a hex editor I discovered the PMT table
entries and the PES packets associated with the data stream no longer match the
input file and are also no longer consistent with "MISB ST 1402 (27 February
2014)".

The code is being built against V11.4.

I have observed the following specific differences.

1) The PMT information is being changed for every PMT entry in the file.

The PMT entries in the input file are the following hex bytes

    0x 15 E0 FC F0 16 26 09 01 00 FF 4B 4C 56 41 00 0F 27 09 C0 75 30 C0 00 14
C0 00 00 1B E0 E0 F0 00 03 E0 C0 F0 00

The PMT entry bytes can be split into three sections

    a) data = 0x 15 E0 FC F0 16 26 09 01 00 FF 4B 4C 56 41 00 0F 27 09 C0 75 30
C0 00 14 C0 00 00
    b) video = 0x 1B E0 E0 F0 00
    c) audio = 0x 03 E0 C0 F0 00

In the output file, the PMT entries have been changed to

    0x 06 E0 FC F0 06 05 4B 4C 56 41 1B E0 E0 F0 00 03 E0 C0 F0 00

    a) data = 0x 06 E0 FC F0 00
    b) video = 0x 1B E0 E0 F0 00
    c) audio = 0x 03 E0 C0 F0 00

The first issue is the 'stream type' byte has changed from 0x15 to 0x06. This
indicates that the data is now an asynchronous stream instead of a synchronous
stream. There is an issue with the 'stream ID' byte (3rd byte = "0xFC"). MISB
ST 1402 states that for synchronous metadata, the stream type shall be 0x15 and
the stream ID shall be 0xFC, for asynchronous metadata, the stream type shall
be 0x06 and the stream ID shall be 0xBD.

2) The PES packet information is being changed for all of the data packets.

In the source file, the first PES packet of the data stream consists of the
following hex bytes.

    0x 47 40 FC 1f 00 00 01 FC 00 E4 81 80 05 25 DE F1 DA E1 00 42 DF 00 D7 06
0E 2B 34

The first four bytes are the packet header, the last four bytes shown (0x 06 0E
2B 34) are the start of the klv data block. the remaining data is the PES
header.

In the output file, the first PES packet of the data stream consists of the
following hex bytes.

    0x 47 40 FC 10 00 00 01 BD 00 E4 81 80 05 25 DE F1 DA E1 00 42 DF 00 D7 06
0E 2B 34

The first four bytes (0x 47 40 FC 30) are the packet header, the last four
bytes shown (0x 06 0E 2B 34) are the start of the klv data block. The remaining
bytes contain the PES Header.

It can clearly be seen that in the the output file that there are additional
bytes directly after the header (0x 01 40). I am not certain what these
represent, but This represents a the PES header contains additional bytes PES
Header (0x 00 00 01 FC ... ), byte 4 has changed from 0xFC (synchronous
metadata) to 0xBD (asynchronous metadata)

In both input and output files, there is a 5 byte PTS value ( 0x 25 DE F1 B0 B1
). There should not be any PTS bytes for asynchronous data (based on setting in
PMT).


The questions I have are as follows.

A) Is my methodology correct? Is this method of opening the file, reading all
packets one at a time and writing to the output file an acceptable way to use
the FFMPEG C++ libraries? Is my code correct, or are some function calls
incorrect and alternative calls should be used in their place.

B) Given that I am reading in each packet in turn and writing it to the output
file, why is the data stream 'synchronous' type being changed to
'asynchronous'? Have I done something wrong when creating the streams for the
output AVFormatContext?

C) If the stream type in the output file is being output as asynchronous (due
to values being written into the PMT), why is a PTS being output in the PES
header of the data packets?

-- 
You are receiving this mail because:
You are watching all bug changes.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.libav.org/pipermail/libav-bugs/attachments/20150915/e3674ab8/attachment.html>


More information about the libav-bugs mailing list