Dear ffmpeg community,

I am using the libav... libraries to build a system that encodes V210
raw video images lossless to a video file on a FTP server.
The application compiles and runs on a CentOS 5 X86_64 Linux system.

Therefore I chose the nut container format as it does not require
"seeking" in output. Also the FFV1 codec was choosen with the
pixel format AV_PIX_FMT_YUV422P10 because this setting is supposed to
reproduce exactly the original YCbCr422-10bit components values
for Y, Cb and Cr.

The system is now running in so far, video format is 1920x1080p25.
The encoding and muxing produces video files, both local or on
a FTP server.

But playing this files with ffplay or with VLC player reveals
a problem. The frames seem to display only every other pixel line
correctly, the incorrect lines are rendered as uniform green pixels.

Tests with v210 codec, or with a local .mov file instead of FTP and
same pixel format showed the same problems.

I converted the video to images with:
ffmpeg -i testvideo.nut -f image2 img%05d.png
to separate images and analysed the pictures as follows:

The 1st 5 pixels of every line seem to have correct properties.
The 6th pixel of the "bad" pixel lines seems to have some other
color as it is the case with the very last pixel of the "good"
lines. The remaining pixels of the "bad" lines all have same
dark green color.

Please find attached the software modules
"VideoCoderTest.c", "VideoCoder.h" and "VideoCoder.c".
When the application is run, a file "testvideo.nut" should be
generated. "ffmpeg_libav.log" contains the command, which was used
to compile the test application. Further it shows the outputs of
execution.

Please view the image of the very 1st frame.
https://www.dropbox.com/s/h7u7bm0qgp93na4/img00001.png?dl=0

Questions - Where is my error?
Wrong initialization of format and codec contexts?
Wrong initialization of output stream?
Wrong allocation and usage of the AVFrame object?
Wrong parameters in format-, codec-, stream-contexts?
Wrong parameter setting in AVFrame object?
May be a timing problem between application and threads in libav?

Is there a general problem in using nut container with either v210
or ffv1 codecs for the above mentioned pixel format?

Every help using this great library is very appreciated.

May be, when my problems get solved, this test could be placed in
your "API examples" folder (after adding more comments by me).

Best regards
Bernhard Dirr
/*******************************************************************************
(c) NewTec GmbH 2015
$Author: dirr $
*******************************************************************************/

#include <stdlib.h>
#include "VideoCoder.h"

#define STREAM_FRAME_RATE       (25) /* 25 images/s */
#define STREAM_PIX_FMT          AV_PIX_FMT_YUV422P10 /* output pix_fmt */
#define CONTAINER_EXTENSION     ".nut"
#define STREAM_CODECID  AV_CODEC_ID_FFV1        /* FF Video 1 Codec Id */
//#define       STREAM_CODECID  AV_CODEC_ID_V210        /* V210 Codec Id */

#ifdef DOINFOLOG
#define THE_AV_LOGLEVEL (AV_LOG_VERBOSE)        /* Loglevel */
#else
//#define       THE_AV_LOGLEVEL (AV_LOG_INFO)           /* Loglevel (default) */
#define THE_AV_LOGLEVEL (AV_LOG_WARNING)        /* Loglevel Warning */
#endif

        VideoCoder::VideoCoder (void)
{
        memset(this, 0, sizeof(VideoCoder));
        //av_log_set_level(AV_LOG_DEBUG);
        av_log_set_level(THE_AV_LOGLEVEL);
}

        VideoCoder::~VideoCoder (void)
{
}

VideoCoder::Ret         VideoCoder::open (
        int             fWidth,
        int             fHeight,
        const char      *ftpAddress,
        const char      *ftpUser,
        const char      *ftpPasswd,
        const char      *fileNameBase)
{
        #define FTPSPEC_SIZE    (100)
        char    aFtpspec[FTPSPEC_SIZE];
        int     ret;

        mWidth = fWidth;
        mHeight = fHeight;

#if 0   // use FTP
        /*FA Build FTP-Spec. */
        // ftp://[user[:password]@]server[:port]/path/to/remote/resource.mpeg
        if (FTPSPEC_SIZE <= snprintf(
                aFtpspec, FTPSPEC_SIZE, "ftp://%s:%s@%s/%s"; CONTAINER_EXTENSION,
                ftpUser, ftpPasswd, ftpAddress, fileNameBase))
        {
                return RET_EPARAM;
        }
#else   // use local file
        /*FA Build File-Spec. */
        if (FTPSPEC_SIZE <= snprintf(
                aFtpspec, FTPSPEC_SIZE, "%s" CONTAINER_EXTENSION, fileNameBase))
        {
                return RET_EPARAM;
        }
#endif  // use FTP

        /*FA Initialize libavcodec, and register all codecs and formats. */
        av_register_all();

        /*FA Initialize network for formats. */
        if (0 > avformat_network_init())
        {
                return RET_EINIT;
        }

        // Find encoder.
        mCodec = avcodec_find_encoder(STREAM_CODECID);
        if (!mCodec)
        {
                av_log(NULL, AV_LOG_ERROR, "%s[%d]no codec\n",
                       __FILE__, __LINE__);
                return RET_EFORMAT;
        }


        /*FA Allocate output media context (should use NUT Muxer). */
        avformat_alloc_output_context2(&mFormatCtx, NULL, NULL, aFtpspec);
        if (!mFormatCtx)
        {
                av_log(NULL, AV_LOG_ERROR, "%s[%d]no output format context\n",
                       __FILE__, __LINE__);
                return RET_EFORMAT;
        }

        /*FA Set Codec */
        av_format_set_video_codec(mFormatCtx, mCodec);

        /*FA Add Stream to OutputFormat. */
        mVideoStream = avformat_new_stream(mFormatCtx, mCodec);
        if (!mVideoStream)
        {
                av_log(NULL, AV_LOG_ERROR, "%s[%d]no stream in output format\n",
                       __FILE__, __LINE__);
                return RET_EFORMAT;
        }

        /*FA Set Stream-Properties. */
        mVideoStream->sample_aspect_ratio = (AVRational){ 1, 1 };
        //mVideoStream->time_base = (AVRational){ 1, STREAM_FRAME_RATE };
        mVideoStream->time_base.num = 1;
        mVideoStream->time_base.den = STREAM_FRAME_RATE;
        /* For fixed-fps content,
         * timebase should be 1/framerate and timestamp increments should be
         * identical to 1. */
        mVideoStream->id = mFormatCtx->nb_streams-1; // Stream-ID

        /*FA Set Codec-Context in Stream. */
        mCodecCtx = mVideoStream->codec;
        mCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;     // ?
        mCodecCtx->codec_id = STREAM_CODECID;           // ?
        mCodecCtx->width = fWidth;
        mCodecCtx->height = fHeight;
        mCodecCtx->time_base = mVideoStream->time_base;
        mCodecCtx->gop_size = 0;
        mCodecCtx->pix_fmt = STREAM_PIX_FMT;
        mCodecCtx->sample_aspect_ratio = (AVRational){ 1, 1 };

        /*FA Open Codec. */
        if (0 > avcodec_open2(mCodecCtx, mCodec, NULL))
        {
                av_log(NULL, AV_LOG_ERROR, "%s[%d]err open codec_ctx\n",
                       __FILE__, __LINE__);
                return RET_ECONVERT;
        }

        /*FA Init Frame-Counter (== PTS). */
        mFrameCounter = 0;

        /*FA Open the output file, if needed. */
        if (!(mFormatCtx->oformat->flags & AVFMT_NOFILE))
        {
                ret = avio_open(&mFormatCtx->pb, aFtpspec, AVIO_FLAG_WRITE);
                if (ret < 0)
                {
                        av_log(NULL, AV_LOG_ERROR, "%s[%d]Could not open '%s': 
%s\n",
                               __FILE__, __LINE__, aFtpspec, av_err2str(ret));
                        return RET_EFORMAT;
                }
        }

        /*FA Allocate frame for feeding image to codec. */
        mFrame = av_frame_alloc();
        if (!mFrame)
        {
                av_log(NULL, AV_LOG_ERROR, "%s[%d]Could not allocate video 
frame\n",
                       __FILE__, __LINE__);
                return RET_EMEM;
        }

        /*FA Set Frame Properties. */
        mFrame->format = STREAM_PIX_FMT;
        mFrame->width = mWidth;
        mFrame->height = mHeight;
        mFrame->pict_type = AV_PICTURE_TYPE_I;
        mFrame->key_frame = 1;
        mFrame->interlaced_frame = 0;
        mFrame->sample_aspect_ratio = (AVRational){ 1, 1 };
        ret = av_frame_get_buffer(mFrame, 32);
        if (ret < 0)
        {
                av_log(NULL, AV_LOG_ERROR,
                       "%s[%d]Could not allocate raw picture buffer\n",
                       __FILE__, __LINE__);
                return RET_EMEM;
        }

        /*FA Some formats want stream headers to be separate. */
        if (mFormatCtx->oformat->flags & AVFMT_GLOBALHEADER)
        {
                mCodecCtx->flags |= CODEC_FLAG_GLOBAL_HEADER;
        }

        av_dump_format(mFormatCtx, mVideoStream->id, aFtpspec, 1);

        /*FA init muxer, write output file header */
        if (avformat_write_header(mFormatCtx, NULL) < 0)
        {
                av_log(NULL, AV_LOG_ERROR, "%s[%d]Error open output file\n",
                       __FILE__, __LINE__);
                return RET_EFORMAT;
        }

        av_log_set_level(THE_AV_LOGLEVEL);

        return RET_OK;
}

VideoCoder::Ret         VideoCoder::putFrame (void)
{
        /*FA Convert V410-Array to Frame. */
        if (RET_OK != decodeV410(mFrame))
        {
                av_log(NULL, AV_LOG_ERROR, "%s[%d]err decode V410 array\n",
                       __FILE__, __LINE__);
                return RET_ECONVERT;
        }

        /*FA Set Timestamp (pts). */
        mFrame->pts = mFrameCounter++;

        /*FA Encode the Frame to the Output Muxer. */
        if (RET_OK != encode_write_frame(mFrame, NULL))
        {
                return RET_EFORMAT;
        }

        return RET_OK;
}

VideoCoder::Ret         VideoCoder::close (void)
{
        VideoCoder::Ret aRet;
        int             gotData;

        /*FA Flush remainig data in codec to output muxer. */
        do
        {
                aRet = encode_write_frame(NULL, &gotData);
        } while ((RET_OK == aRet) && gotData);

        /*FA Write End-Data to Output. */
        if (0 != av_write_trailer(mFormatCtx))
        {
                /* ... */
        }

        /*FA Is output file to be closed? */
        if (!(mFormatCtx->oformat->flags & AVFMT_NOFILE))
        {
                /*FA Close the output file. */
                avio_close(mFormatCtx->pb);
        }

        // User is required to call avcodec_close() and avformat_free_context()
        // to clean up the allocation by avformat_new_stream().

        /*FA Close Codec. */
        if (0 != avcodec_close(mCodecCtx))
        {
                /* ... */
        }

        /*FA Close Muxer. */
        avformat_free_context(mFormatCtx);
        {
                /* ... */
        }

        /*FA Free the Frame. */
        //av_freep(&mFrame->data[0]);
        av_frame_free(&mFrame);

        /*FA Deinitialize network for formats. */
        if (0 > avformat_network_deinit())
        {
                return RET_EINIT;
        }

        return RET_OK;
}

VideoCoder::Ret         VideoCoder::encode_write_frame(
        AVFrame         *outFrame,
        int             *gotData)
{
        int             ret;
        int             gotOutput;
        AVPacket        enc_pkt;

        av_log(NULL, AV_LOG_DEBUG, "%s[%d]Encoding frame\n", __FILE__, 
__LINE__);

        if (0 == gotData)
        {
                gotData = &gotOutput;
        }
        *gotData = 0;

        /*FA Flush only? */
        if (NULL == outFrame)
        {
                /*FA Flush remaining data in codec to output muxer. */
                if (!(mFormatCtx->streams[0]->codec->codec->capabilities
                      & CODEC_CAP_DELAY))
                {
                        av_log(NULL, AV_LOG_VERBOSE,
                               "%s[%d]Flushing no delayed data\n",
                               __FILE__, __LINE__);
                        /*FA No data remaining in codec. */
                        return RET_OK;
                }
                av_log(NULL, AV_LOG_VERBOSE, "%s[%d]Flushing w/o frame\n",
                       __FILE__, __LINE__);
        }

        /*FA Prepare new Packet */
        enc_pkt.data = NULL;
        enc_pkt.size = 0;
        av_init_packet(&enc_pkt);

        /*FA Encode frame to packet. */
        ret = avcodec_encode_video2(
                mFormatCtx->streams[0]->codec, &enc_pkt, outFrame, gotData);
        if (ret < 0)
        {
                return RET_ECONVERT;
        }
        if (!(*gotData))
        {
                return RET_OK;
        }

        /*FA Set Stream-Index and adjust Timestamp in packet for muxing. */
        enc_pkt.stream_index = 0;
        #if 1 // noch nicht in Version 2.2.x
        av_packet_rescale_ts(&enc_pkt,  // ? noetig ?
                             mFormatCtx->streams[0]->codec->time_base,
                             mFormatCtx->streams[0]->time_base);
        av_pkt_dump_log2(NULL, AV_LOG_VERBOSE, &enc_pkt, 0, mVideoStream);
        //log_packet(mFormatCtx, &enc_pkt);
        #else
        rescale_timestamps(&mCodecCtx->time_base, mVideoStream, &enc_pkt);
        //log_packet(mFormatCtx, &enc_pkt);
        #endif // noch nicht in Version 2.2.x

        /*FA Feed packet to output muxer. */
        av_log(NULL, AV_LOG_DEBUG, "%s[%d]Muxing frame\n", __FILE__, __LINE__);
        ret = av_interleaved_write_frame(mFormatCtx, &enc_pkt);
        if (ret < 0)
        {
                return RET_EFORMAT;
        }

        return RET_OK;
}

VideoCoder::Ret         VideoCoder::decodeV410 (
        AVFrame         *avFrame)
{
        int             aYPos, aXPos, aWidth;
        uint16_t        *y, *u, *v;
        const uint32_t  *src;

        if (0 > av_frame_make_writable(avFrame))
        {
                av_log(NULL, AV_LOG_ERROR, "%s[%d]not writable\n",
                       __FILE__, __LINE__);
        }

        // Round down width to multiples of 6 (Pixel).
        aWidth = (mWidth / 6) * 6;

        // BT.709 10-Bit-Range
        #define YMIN    (64)
        #define YMAX    (940)
        #define UVMIN   (64)
        #define UVMAX   (960)
        #define UVGREY  (512)

        #define NEXTY(y)        ((y) >= YMAX ? (y) = YMIN : (y)++)
        #define NEXTUV(uv)      ((uv) >= UVMAX ? (uv) = UVMIN : (uv)++)

        static  uint16_t        testy = YMIN;
        static  uint16_t        testuv = UVGREY;

        uint16_t        ystart = testy;
        uint16_t        uvstart = testuv;

        NEXTY(testy);
        NEXTUV(testuv);

        /*FA Fill Dummy-Data to Frame. */
        for (aYPos = 0; aYPos < mHeight; aYPos++)
        {
                uint16_t        yval, uvval;
                unsigned char   *aPtr;

                yval = ystart; NEXTY(ystart);
                uvval = uvstart; NEXTUV(uvstart);

                /*FA Set Pointer to Component Planes. */
                #if 1
                aPtr = avFrame->data[0] + aYPos * avFrame->linesize[0];
                y = (uint16_t *)aPtr;
                aPtr = avFrame->data[1] + aYPos * avFrame->linesize[1];
                u = (uint16_t *)aPtr;
                aPtr = avFrame->data[2] + aYPos * avFrame->linesize[2];
                v = (uint16_t *)aPtr;
                #else
                y = (uint16_t*)avFrame->data[0]
                        + aYPos * (avFrame->linesize[0] / 2);
                u = (uint16_t*)avFrame->data[1]
                        + aYPos * (avFrame->linesize[1] / 2);
                v = (uint16_t*)avFrame->data[2]
                        + aYPos * (avFrame->linesize[2] / 2);
                #endif

                for (aXPos = 0; aXPos < aWidth - 5; aXPos += 6)
                {
                        // 1. 32-Bit (uyv)
                        *u++ = uvval;
                        *y++ = yval;
                        *v++ = uvval;
                        //NEXTUV(uvval);
                        NEXTY(yval);

                        // 2. 32-Bit (-y-)
                        *y++ = yval;
                        NEXTY(yval);

                        // 3. 32-Bit (uyv)
                        *u++ = uvval;
                        *y++ = yval;
                        *v++ = uvval;
                        //NEXTUV(uvval);
                        NEXTY(yval);

                        // 4. 32-Bit (-y-)
                        *y++ = yval;
                        NEXTY(yval);

                        // 5. 32-Bit (uyv)
                        *u++ = uvval;
                        *y++ = yval;
                        *v++ = uvval;
                        //NEXTUV(uvval);
                        NEXTY(yval);

                        // 6. 32-Bit (-y-)
                        *y++ = yval;
                        NEXTY(yval);
                } // Loop aXPos

                // Handle remaining pixels of line
                aWidth = mWidth - aWidth;
                if (aWidth > 0)
                {
                        aWidth--; // Pix. 0
                        // 1. 32-Bit (uyv)
                        *u++ = uvval;
                        *y++ = yval;
                        *v++ = uvval;
                        //NEXTUV(uvval);
                        NEXTY(yval);
                }
                if (aWidth > 0)
                {
                        aWidth--; // Pix. 1
                        // 2. 32-Bit (-y-)
                        *y++ = yval;
                        NEXTY(yval);
                }
                if (aWidth > 0)
                {
                        aWidth--; // Pix. 2
                        // 3. 32-Bit (uyv)
                        *u++ = uvval;
                        *y++ = yval;
                        *v++ = uvval;
                        //NEXTUV(uvval);
                        NEXTY(yval);
                }
                if (aWidth > 0)
                {
                        aWidth--; // Pix. 3
                        // 4. 32-Bit (-y-)
                        *y++ = yval;
                        NEXTY(yval);
                }
                if (aWidth > 0)
                {
                        aWidth--; // Pix. 4
                        // 5. 32-Bit (uyv)
                        *u++ = uvval;
                        *y++ = yval;
                        *v++ = uvval;
                        //NEXTUV(uvval);
                        NEXTY(yval);
                }
        } // Loop aYPos

        return RET_OK;
}

/*******************************************************************************
(c) NewTec GmbH 2015
Author: Bernhard Dirr
*******************************************************************************/
#ifndef  VIDEOCODER_H
# define VIDEOCODER_H

#ifdef __cplusplus
extern "C" {
#endif

#if 1
#if !defined __STDC_CONSTANT_MACROS     // needed for libav....
#define __STDC_CONSTANT_MACROS
#endif
#if !defined __STDC_FORMAT_MACROS       // needed for libav....
#define __STDC_FORMAT_MACROS
#endif

#ifndef INT64_C
#define INT64_C(c) (c ## LL)
#define UINT64_C(c) (c ## ULL)
#endif
#endif

#include <libavutil/opt.h>
#include <libavutil/timestamp.h>
#include <libavutil/imgutils.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavcodec/avcodec.h>

#ifdef __cplusplus
}
#endif

class   VideoCoder
{
public:
        typedef enum Ret
        {
                 RET_OK = 0
                ,RET_ECONVERT
                ,RET_EFORMAT
                ,RET_EMEM
                ,RET_EPARAM
                ,RET_EINIT
        } Ret;

        VideoCoder (void);
        ~VideoCoder (void);

        VideoCoder::Ret         open (
                int             fWidth,
                int             fHeight,
                const char      *ftpAddress,
                const char      *ftpUser,
                const char      *ftpPasswd,
                const char      *fileNameBase);

        VideoCoder::Ret         putFrame (void);

        VideoCoder::Ret         close (void);

protected:

private:

        AVOutputFormat  *mOFormat;
        AVFormatContext *mFormatCtx;
        AVCodec         *mCodec;
        AVCodecContext  *mCodecCtx;
        AVStream        *mVideoStream;
        AVFrame         *mFrame;
        int     mHeight;
        int     mWidth;
        int64_t mFrameCounter;

        VideoCoder::Ret         encode_write_frame(
                AVFrame         *outFrame,
                int             *gotData);

        VideoCoder::Ret         decodeV410 (
                AVFrame         *avFrame);
};

#endif

/*******************************************************************************
(c) NewTec GmbH 2015
Author: Bernhard Dirr
*******************************************************************************/

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include "VideoCoder.h"

#define STREAM_FRAME_RATE       (25) /* 25 images/s */
#define STREAM_PIX_FMT          AV_PIX_FMT_YUV422P10 /* output pix_fmt */
#define CONTAINER_EXTENSION     ".nut"
#define STREAM_CODECID  AV_CODEC_ID_FFV1        /* FF Video 1 Codec Id */
//#define       STREAM_CODECID  AV_CODEC_ID_V210        /* V210 Codec Id */

#define THEFRAMECOUNT   (STREAM_FRAME_RATE * 3) // 3s Video

#ifdef DOINFOLOG
#define THE_AV_LOGLEVEL (AV_LOG_VERBOSE)        /* Loglevel */
#else
//#define       THE_AV_LOGLEVEL (AV_LOG_INFO)           /* Loglevel (default) */
#define THE_AV_LOGLEVEL (AV_LOG_WARNING)        /* Loglevel Warnung */
#endif

#define THESERVER       "127.0.0.1"
#define THEUSER         "ipuuser"
#define THEPASSWORD     "changeMe"
#define THEFILE         "testvideo"

int     main (int argc, char *argv[])
{
        VideoCoder      theCoder;
        int             aCount;

        if (theCoder.open(1920, 1080, THESERVER, THEUSER, THEPASSWORD, THEFILE)
            != VideoCoder::RET_OK)
        {
                return -1;
        }

        for (aCount = 0; aCount < THEFRAMECOUNT; aCount++)
        {
                if (theCoder.putFrame() != VideoCoder::RET_OK)
                {
                        break;
                }
        }

        if (theCoder.close() != VideoCoder::RET_OK)
        {
                return -1;
        }

        return 0;
}

Attachment: ffmpeg_libav.log
Description: ffmpeg_libav.log

_______________________________________________
Libav-user mailing list
[email protected]
http://ffmpeg.org/mailman/listinfo/libav-user

Reply via email to