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;
}
ffmpeg_libav.log
Description: ffmpeg_libav.log
_______________________________________________ Libav-user mailing list [email protected] http://ffmpeg.org/mailman/listinfo/libav-user
