16 #include <libswscale/swscale.h>
17 #include <libavformat/avformat.h>
18 #include <libavcodec/avcodec.h>
19 #include <libavcodec/version.h>
20 #include <libavutil/pixfmt.h>
21 #include <libavutil/mathematics.h>
33 template <
typename T1>
34 T
operator()(
const T1& x)
const {
return static_cast<T
>(x); }
54 template<
class ContainerV
ideo,
typename T, std::
size_t Nb_components>
70 :
base(),pFormatCtx(NULL),video_stream_idx_(-1),pictureIN(NULL),pictureOUT(NULL),pSwsCtxOUT(NULL),
71 codec_id_(CODEC_ID_NONE),dimension_optimize_(0),bit_rate_(0),output_video_width_(0),output_video_height_(0),
72 output_video_nbframes_(0),time_base_((AVRational){0,1}),input_pxfmt_(PIX_FMT_GRAY8),
73 output_pxfmt_(PIX_FMT_RGB24),row_stride(0),finished(0),total_nb_slice(0),
initialized(
false){}
92 AvWriter(std::string output_filename, std::size_t output_video_width, std::size_t output_video_height,
93 std::size_t output_video_nbframes,
int bit_rate, AVRational time_base,
bool dimension_optimize = 0,
94 CodecID codec_id = CODEC_ID_NONE)
95 :
base(output_filename),pFormatCtx(NULL),video_stream_idx_(-1),pictureIN(NULL),pictureOUT(NULL),pSwsCtxOUT(NULL),
96 codec_id_(codec_id),dimension_optimize_(dimension_optimize), bit_rate_(bit_rate),output_video_width_(output_video_width),
97 output_video_height_(output_video_height),output_video_nbframes_(output_video_nbframes),time_base_(time_base),
98 input_pxfmt_(PIX_FMT_GRAY8),output_pxfmt_(PIX_FMT_RGB24),row_stride(0),finished(0),total_nb_slice(0),initialized(false)
127 void initialize( std::size_t output_video_width, std::size_t output_video_height,
128 std::size_t output_video_nbframes,
int bit_rate, AVRational time_base,
129 bool dimension_optimize = 0, CodecID codec_id = CODEC_ID_NONE);
150 AVFormatContext *pFormatCtx;
151 int video_stream_idx_;
155 AVFrame * pictureOUT;
157 struct SwsContext * pSwsCtxOUT;
163 bool dimension_optimize_;
167 std::size_t output_video_width_;
169 std::size_t output_video_height_;
171 std::size_t output_video_nbframes_;
173 AVRational time_base_;
175 PixelFormat input_pxfmt_;
177 PixelFormat output_pxfmt_;
183 std::size_t total_nb_slice;
189 void alloc_picture(AVFrame *&picture, PixelFormat pix_fmt,
int width,
int height);
190 void add_video_stream();
191 void write_video_frame(
const container_type & data, std::size_t nb_slice);
198 template<
class ContainerV
ideo,
typename T, std::
size_t Nb_components>
201 bit_rate_ = bit_rate;
202 output_video_width_ = output_video_width;
203 output_video_height_ = output_video_height;
204 output_video_nbframes_ = output_video_nbframes;
205 time_base_ = time_base;
206 codec_id_ = codec_id;
207 dimension_optimize_ = dimension_optimize;
210 template<
class ContainerV
ideo,
typename T, std::
size_t Nb_components>
215 uint8_t *picture_buf;
218 picture = avcodec_alloc_frame();
220 size = avpicture_get_size(pix_fmt, width, height);
221 picture_buf =
reinterpret_cast<uint8_t*
>(av_malloc(size));
225 avpicture_fill((AVPicture *)picture, picture_buf, pix_fmt, width, height);
231 template<
class ContainerV
ideo,
typename T, std::
size_t Nb_components>
233 void AvWriter<ContainerVideo,T,Nb_components>::add_video_stream()
235 AVStream * video_st = NULL;
237 AVCodec * codec = avcodec_find_encoder(codec_id_);
239 std::ostringstream err;
241 slip::slip_exception exc(std::string(
"slip"), std::string(
"AvWriter::add_video_stream()"), err.str());
245 const PixelFormat * possible_pix_fmt = codec->pix_fmts;
246 if (possible_pix_fmt){
248 #ifndef FF_API_FIND_BEST_PIX_FMT
249 int64_t pix_fmt_mask = (1 << *possible_pix_fmt);
251 while(*possible_pix_fmt != -1){
252 pix_fmt_mask = pix_fmt_mask || (1 << *possible_pix_fmt);
255 output_pxfmt_ = avcodec_find_best_pix_fmt(pix_fmt_mask,input_pxfmt_,0,&loss);
257 output_pxfmt_ = avcodec_find_best_pix_fmt2(possible_pix_fmt,input_pxfmt_,0,&loss);
261 std::cerr <<
"Warning -> pixel format unknown for this codec, "
262 <<
"PIX_FMT_RGB24 has been chosen by default." << std::endl;
265 video_st = avformat_new_stream(pFormatCtx, codec);
267 std::ostringstream err;
268 err <<
FILE_WRITE_ERROR << this->output_filename_ <<
" | Could not alloc stream.";
269 slip::slip_exception exc(std::string(
"slip"), std::string(
"AvWriter::add_video_stream()"), err.str());
274 video_st->codec->time_base= time_base_;
275 video_st->time_base = time_base_;
276 video_st->codec->gop_size = time_base_.den;
277 video_st->codec->pix_fmt = output_pxfmt_;
278 if (video_st->codec->codec_id == CODEC_ID_MPEG2VIDEO) {
280 video_st->codec->max_b_frames = 2;
282 if (video_st->codec->codec_id == CODEC_ID_MPEG1VIDEO) {
286 video_st->codec->mb_decision = 2;
289 int width =
static_cast<int>(output_video_width_);
290 int height =
static_cast<int>(output_video_height_);
292 if(dimension_optimize_){
293 std::cerr <<
"Old dimensions = (" << width <<
", " << height <<
")\n";
294 int linesize_align[AV_NUM_DATA_POINTERS];
295 avcodec_align_dimensions2(video_st->codec,&width,&height,linesize_align);
296 std::cerr <<
"New dimensions = (" << width <<
", " << height <<
")\n";
298 video_st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
299 video_st->codec->bit_rate = bit_rate_;
300 video_st->codec->width = width;
301 video_st->codec->height = height;
304 if (pFormatCtx->oformat->flags & AVFMT_GLOBALHEADER)
305 video_st->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
307 video_stream_idx_ = video_st->index;
310 if (avcodec_open2(video_st->codec, codec, NULL) < 0) {
311 std::ostringstream err;
312 err <<
FILE_WRITE_ERROR << this->output_filename_ <<
" | Can not open codec.";
313 slip::slip_exception exc(std::string(
"slip"), std::string(
"AvWritter::add_video_stream()"), err.str());
319 alloc_picture(pictureIN, input_pxfmt_, output_video_width_, output_video_height_);
321 std::ostringstream err;
322 err <<
FILE_WRITE_ERROR << this->output_filename_ <<
" | Could not allocate input picture buffer.";
323 slip::slip_exception exc(std::string(
"slip"), std::string(
"AvWritter::add_video_stream()"), err.str());
327 alloc_picture(pictureOUT, video_st->codec->pix_fmt, video_st->codec->width, video_st->codec->height);
329 std::ostringstream err;
330 err <<
FILE_WRITE_ERROR << this->output_filename_ <<
" | Could not allocate output picture buffer.";
331 slip::slip_exception exc(std::string(
"slip"), std::string(
"AvWritter::add_video_stream()"), err.str());
336 template<
class ContainerV
ideo,
typename T, std::
size_t Nb_components>
338 void AvWriter<ContainerVideo,T,Nb_components>::initialize(){
340 if (Nb_components != 1 && Nb_components != 3){
341 std::ostringstream err;
342 err <<
FILE_WRITE_ERROR << this->output_filename_ <<
" | Nb_components should be 1 or 3.";
343 slip::slip_exception exc(std::string(
"slip"), std::string(
"AvWriter::initialize()"), err.str());
346 if (Nb_components == 3){
347 input_pxfmt_ = PIX_FMT_RGB24;
350 avcodec_register_all();
355 AVOutputFormat *fmt = av_guess_format(NULL, this->output_filename_.c_str(), NULL);
357 std::cerr <<
"Could not deduce output format from file extension: using MPEG." << std::endl;
358 fmt = av_guess_format(
"mpeg", NULL, NULL);
361 std::ostringstream err;
362 err <<
FILE_WRITE_ERROR << this->output_filename_ <<
" | Could not find suitable output format.";
363 slip::slip_exception exc(std::string(
"slip"), std::string(
"AvWriter::initialize()"), err.str());
369 if(codec_id_ != fmt->video_codec && codec_id_ != CODEC_ID_NONE){
370 std::cerr <<
"The specified codec id does not match the file extension." << std::endl;
372 codec_id_ = fmt->video_codec;
374 pFormatCtx = avformat_alloc_context();
376 std::ostringstream err;
378 slip::slip_exception exc(std::string(
"slip"), std::string(
"AvWriter::initialize()"), err.str());
381 pFormatCtx->oformat = fmt;
382 strcpy(pFormatCtx->filename, this->output_filename_.c_str());
387 if (pFormatCtx->oformat->video_codec != CODEC_ID_NONE) {
391 av_dump_format(pFormatCtx, 0, this->output_filename_.c_str(), 1);
393 if (!(pFormatCtx->oformat->flags & AVFMT_NOFILE)) {
395 if (avio_open(&pFormatCtx->pb, this->output_filename_.c_str(), AVIO_FLAG_WRITE) < 0) {
396 std::ostringstream err;
398 slip::slip_exception exc(std::string(
"slip"), std::string(
"AvWriter::initialize()"), err.str());
404 if (avformat_write_header(pFormatCtx, NULL) < 0){
405 std::ostringstream err;
406 err <<
FILE_WRITE_ERROR << this->output_filename_ <<
" | Cannot write a file header.";
407 slip::slip_exception exc(std::string(
"slip"), std::string(
"AvWriter::initialize()"), err.str());
410 row_stride = output_video_width_ * Nb_components;
415 template<
class ContainerV
ideo,
typename T, std::
size_t Nb_components>
417 void AvWriter<ContainerVideo,T,Nb_components>::write_video_frame(
const container_type & data, std::size_t nb_slice)
420 AVStream * video_st = pFormatCtx->streams[video_stream_idx_];
421 AVCodecContext * pCodecCtx = video_st->codec;
423 if (Nb_components == 1){
424 pSwsCtxOUT = sws_getContext(data.cols(),data.rows(),PIX_FMT_GRAY8,pCodecCtx->width,pCodecCtx->height,
425 pCodecCtx->pix_fmt,SWS_FAST_BILINEAR, NULL, NULL, NULL);
427 pSwsCtxOUT = sws_getContext(data.cols(),data.rows(),PIX_FMT_RGB24,pCodecCtx->width,pCodecCtx->height,
428 pCodecCtx->pix_fmt,SWS_FAST_BILINEAR, NULL, NULL, NULL);
430 if (pSwsCtxOUT == NULL) {
431 std::ostringstream err;
432 err <<
FILE_WRITE_ERROR << this->output_filename_ <<
" | Cannot initialize the YUV conversion context.";
433 slip::slip_exception exc(std::string(
"slip"), std::string(
"AvWriter::write_video_frame()"), err.str());
437 if (total_nb_slice >= output_video_nbframes_) {
442 for(std::size_t y=0; y < data.rows(); y++) {
443 const T * data_ptr_i =
reinterpret_cast<const T *
>(data[nb_slice][y]);
444 std::transform(data_ptr_i, data_ptr_i + row_stride,
448 sws_scale(pSwsCtxOUT, pictureIN->data, pictureIN->linesize,0, data.rows(), pictureOUT->data,
449 pictureOUT->linesize);
453 static_cast<double>(total_nb_slice);
455 if (pFormatCtx->oformat->flags & AVFMT_RAWPICTURE) {
459 av_init_packet(&pkt);
461 pkt.flags |= AV_PKT_FLAG_KEY;
462 pkt.stream_index = video_stream_idx_;
463 pkt.data =
reinterpret_cast<uint8_t *
>(pictureOUT);
464 pkt.size =
sizeof(AVPicture);
466 ret = av_write_frame(pFormatCtx, &pkt);
467 av_free_packet(&pkt);
469 AVPacket pkt = { 0 };
470 av_init_packet(&pkt);
472 #ifndef FF_API_OLD_ENCODE_VIDEO
473 int buf_size =
static_cast<int>( 9 * video_st->codec->width * video_st->codec->height * Nb_components + 10000);
475 uint8_t * buf =
new uint8_t[buf_size];
476 int sizeout = avcodec_encode_video(pCodecCtx, buf, buf_size, pictureOUT);
480 if (pCodecCtx->coded_frame->pts != static_cast<int64_t>(AV_NOPTS_VALUE))
481 pkt.pts= av_rescale_q(pCodecCtx->coded_frame->pts, pCodecCtx->time_base, video_st->time_base);
482 if(pCodecCtx->coded_frame->key_frame)
483 pkt.flags |= AV_PKT_FLAG_KEY;
484 pkt.stream_index = video_st->index;
486 ret = av_write_frame(pFormatCtx, &pkt);
492 ret = avcodec_encode_video2(pCodecCtx, &pkt, pictureOUT, &got_packet);
493 if(!ret && got_packet && pkt.size){
494 if (pkt.pts != static_cast<int64_t>(AV_NOPTS_VALUE)) {
495 pkt.pts = av_rescale_q(pkt.pts,
496 pCodecCtx->time_base, video_st->time_base);
498 if (pkt.dts != static_cast<int64_t>(AV_NOPTS_VALUE)) {
499 pkt.dts = av_rescale_q(pkt.dts,
500 pCodecCtx->time_base, video_st->time_base);
503 pkt.stream_index = video_st->index;
505 ret = av_write_frame(pFormatCtx, &pkt);
510 av_free_packet(&pkt);
515 std::ostringstream err;
516 err <<
FILE_WRITE_ERROR << this->output_filename_ <<
" | Error while writing frame.";
525 template<
class ContainerV
ideo,
typename T, std::
size_t Nb_components>
529 std::ostringstream err;
530 err <<
FILE_WRITE_ERROR << this->output_filename_ <<
" | The writer to be initialized before writing.";
538 std::size_t nb_slices = data.slices();
539 if(output_video_nbframes_ < (total_nb_slice + nb_slices)){
540 std::cerr <<
"output_video_nbframes_ = " << output_video_nbframes_ << std::endl;
541 std::cerr <<
"total_nb_slice = " << total_nb_slice << std::endl;
542 std::cerr <<
"nb_slices = " << nb_slices << std::endl;
543 std::ostringstream err;
544 err <<
FILE_WRITE_ERROR << this->output_filename_ <<
" | Allocation problem during writing.";
550 for(std::size_t i = 0; i < nb_slices; i++) {
551 write_video_frame(data,i);
554 if (total_nb_slice == output_video_nbframes_){
555 if(pFormatCtx->streams[video_stream_idx_]){
556 double time_base_dble =
static_cast<double>(pFormatCtx->streams[video_stream_idx_]->time_base.num) /
557 static_cast<double>(pFormatCtx->streams[video_stream_idx_]->time_base.den);
558 double video_pts =
static_cast<double>(pFormatCtx->streams[video_stream_idx_]->pts.val) * time_base_dble;
559 double duration =
static_cast<double>(output_video_nbframes_) * time_base_dble;
560 while(video_pts < duration){
561 write_video_frame(data,0);
562 video_pts =
static_cast<double>(pFormatCtx->streams[video_stream_idx_]->pts.val) * time_base_dble ;
565 av_write_trailer(pFormatCtx);
572 template<
class ContainerV
ideo,
typename T, std::
size_t Nb_components>
578 av_free(pictureIN->data[0]);
580 av_free(pictureOUT->data[0]);
582 if (!(pFormatCtx->oformat->flags & AVFMT_NOFILE))
584 avio_close(pFormatCtx->pb);
586 for (std::size_t i = 0; i < pFormatCtx->nb_streams; i++) {
587 av_freep(&pFormatCtx->streams[i]->codec);
588 av_freep(&pFormatCtx->streams[i]);
594 video_stream_idx_ = -1;
595 codec_id_ = CODEC_ID_NONE;
596 dimension_optimize_ =
false;
598 output_video_width_ = 0;
599 output_video_height_ = 0;
600 output_video_nbframes_ = 0;
601 time_base_ = (AVRational){0,1};
602 input_pxfmt_ = PIX_FMT_GRAY8;
603 output_pxfmt_ = PIX_FMT_RGB24;
AvWriter is a video writer.
const std::string FILE_WRITE_ERROR
standard exception extension name have been changed to eliminate conflicts with QT ...
Contains the container writer base class.
void release()
release the writing process. Has to be called when an video has been fully written.
void initialize(std::size_t output_video_width, std::size_t output_video_height, std::size_t output_video_nbframes, int bit_rate, AVRational time_base, bool dimension_optimize=0, CodecID codec_id=CODEC_ID_NONE)
initialized the writing process. Has to be called before the first write() call of a given video...
Provides some functors to change of color space.
const std::string FILE_OPEN_ERROR
ContainerVideo container_type
ContainerWriter< ContainerVideo, T > base
T operator()(const T1 &x) const
Provides SLIP error messages.
ContainerWriter is the base class of the writer classes. All the writers are working the same way: ...
AvWriter(std::string output_filename, std::size_t output_video_width, std::size_t output_video_height, std::size_t output_video_nbframes, int bit_rate, AVRational time_base, bool dimension_optimize=0, CodecID codec_id=CODEC_ID_NONE)
int write(const container_type &data)
write function. This function write the data within the output file after the previous one...