std::shared_ptr<AVFrame> decode(safe_ptr<AVPacket> pkt) { std::shared_ptr<AVFrame> decoded_frame(avcodec_alloc_frame(), av_free); int frame_finished = 0; THROW_ON_ERROR2(avcodec_decode_video2(codec_context_.get(), decoded_frame.get(), &frame_finished, pkt.get()), "[video_decoder]"); // If a decoder consumes less then the whole packet then something is wrong // that might be just harmless padding at the end, or a problem with the // AVParser or demuxer which puted more then one frame in a AVPacket. if(frame_finished == 0) return nullptr; is_progressive_ = !decoded_frame->interlaced_frame; if(decoded_frame->repeat_pict > 0) CASPAR_LOG(warning) << "[video_decoder] Field repeat_pict not implemented."; ++file_frame_number_; // This ties the life of the decoded_frame to the packet that it came from. For the // current version of ffmpeg (0.8 or c17808c) the RAW_VIDEO codec returns frame data // owned by the packet. return std::shared_ptr<AVFrame>(decoded_frame.get(), [decoded_frame, pkt](AVFrame*){}); }
std::shared_ptr<AVStream> add_audio_stream(std::vector<option>& options) { if(output_format_.acodec == CODEC_ID_NONE) return nullptr; auto st = av_new_stream(oc_.get(), 1); if(!st) BOOST_THROW_EXCEPTION(caspar_exception() << msg_info("Could not allocate audio-stream") << boost::errinfo_api_function("av_new_stream")); auto encoder = avcodec_find_encoder(output_format_.acodec); if (!encoder) BOOST_THROW_EXCEPTION(caspar_exception() << msg_info("codec not found")); auto c = st->codec; avcodec_get_context_defaults3(c, encoder); c->codec_id = output_format_.acodec; c->codec_type = AVMEDIA_TYPE_AUDIO; c->sample_rate = 48000; c->channels = channel_layout_.num_channels; c->sample_fmt = SAMPLE_FMT_S16; if(output_format_.vcodec == CODEC_ID_FLV1) c->sample_rate = 44100; if(output_format_.format->flags & AVFMT_GLOBALHEADER) c->flags |= CODEC_FLAG_GLOBAL_HEADER; boost::range::remove_erase_if(options, [&](const option& o) { return ffmpeg::av_opt_set(c, o.name.c_str(), o.value.c_str(), AV_OPT_SEARCH_CHILDREN) > -1; }); THROW_ON_ERROR2(avcodec_open(c, encoder), "[ffmpeg_consumer]"); return std::shared_ptr<AVStream>(st, [](AVStream* st) { LOG_ON_ERROR2(avcodec_close(st->codec), "[ffmpeg_consumer]");; av_freep(&st->codec); av_freep(&st); }); }
void encode_video_frame(core::read_frame& frame) { auto c = video_st_->codec; auto in_time = static_cast<double>(in_frame_number_) / format_desc_.fps; auto out_time = static_cast<double>(out_frame_number_) / (static_cast<double>(c->time_base.den) / static_cast<double>(c->time_base.num)); in_frame_number_++; if(out_time - in_time > 0.01) return; auto av_frame = convert_video(frame, c); av_frame->interlaced_frame = format_desc_.field_mode != core::field_mode::progressive; av_frame->top_field_first = format_desc_.field_mode == core::field_mode::upper; av_frame->pts = out_frame_number_++; int out_size = THROW_ON_ERROR2(avcodec_encode_video(c, video_outbuf_.data(), video_outbuf_.size(), av_frame.get()), "[ffmpeg_consumer]"); if(out_size == 0) return; safe_ptr<AVPacket> pkt(new AVPacket, [](AVPacket* p) { av_free_packet(p); delete p; }); av_init_packet(pkt.get()); if (c->coded_frame->pts != AV_NOPTS_VALUE) pkt->pts = av_rescale_q(c->coded_frame->pts, c->time_base, video_st_->time_base); if(c->coded_frame->key_frame) pkt->flags |= AV_PKT_FLAG_KEY; pkt->stream_index = video_st_->index; pkt->data = video_outbuf_.data(); pkt->size = out_size; av_interleaved_write_frame(oc_.get(), pkt.get()); }
std::shared_ptr<AVStream> add_video_stream(std::vector<option>& options) { if(output_format_.vcodec == CODEC_ID_NONE) return nullptr; auto st = av_new_stream(oc_.get(), 0); if (!st) BOOST_THROW_EXCEPTION(caspar_exception() << msg_info("Could not allocate video-stream.") << boost::errinfo_api_function("av_new_stream")); auto encoder = avcodec_find_encoder(output_format_.vcodec); if (!encoder) BOOST_THROW_EXCEPTION(caspar_exception() << msg_info("Codec not found.")); auto c = st->codec; avcodec_get_context_defaults3(c, encoder); c->codec_id = output_format_.vcodec; c->codec_type = AVMEDIA_TYPE_VIDEO; c->width = output_format_.width; c->height = output_format_.height; c->time_base.den = format_desc_.time_scale; c->time_base.num = format_desc_.duration; c->gop_size = 25; c->flags |= format_desc_.field_mode == core::field_mode::progressive ? 0 : (CODEC_FLAG_INTERLACED_ME | CODEC_FLAG_INTERLACED_DCT); if(c->pix_fmt == PIX_FMT_NONE) c->pix_fmt = PIX_FMT_YUV420P; if(c->codec_id == CODEC_ID_PRORES) { c->bit_rate = c->width < 1280 ? 63*1000000 : 220*1000000; c->pix_fmt = PIX_FMT_YUV422P10; } else if(c->codec_id == CODEC_ID_DNXHD) { if(c->width < 1280 || c->height < 720) BOOST_THROW_EXCEPTION(caspar_exception() << msg_info("Unsupported video dimensions.")); c->bit_rate = 220*1000000; c->pix_fmt = PIX_FMT_YUV422P; } else if(c->codec_id == CODEC_ID_DVVIDEO) { c->width = c->height == 1280 ? 960 : c->width; if(format_desc_.format == core::video_format::ntsc) c->pix_fmt = PIX_FMT_YUV411P; else if(format_desc_.format == core::video_format::pal) c->pix_fmt = PIX_FMT_YUV420P; else // dv50 c->pix_fmt = PIX_FMT_YUV422P; if(format_desc_.duration == 1001) c->width = c->height == 1080 ? 1280 : c->width; else c->width = c->height == 1080 ? 1440 : c->width; } else if(c->codec_id == CODEC_ID_H264) { c->pix_fmt = PIX_FMT_YUV420P; if(options.empty()) { av_opt_set(c->priv_data, "preset", "ultrafast", 0); av_opt_set(c->priv_data, "tune", "fastdecode", 0); av_opt_set(c->priv_data, "crf", "5", 0); } } else if(c->codec_id == CODEC_ID_QTRLE) { c->pix_fmt = PIX_FMT_ARGB; } c->max_b_frames = 0; // b-frames not supported. boost::range::remove_erase_if(options, [&](const option& o) { return ffmpeg::av_opt_set(c, o.name.c_str(), o.value.c_str(), AV_OPT_SEARCH_CHILDREN) > -1 || ffmpeg::av_opt_set(c->priv_data, o.name.c_str(), o.value.c_str(), AV_OPT_SEARCH_CHILDREN) > -1; }); if(output_format_.format->flags & AVFMT_GLOBALHEADER) c->flags |= CODEC_FLAG_GLOBAL_HEADER; c->thread_count = boost::thread::hardware_concurrency(); if(avcodec_open(c, encoder) < 0) { c->thread_count = 1; THROW_ON_ERROR2(avcodec_open(c, encoder), "[ffmpeg_consumer]"); } return std::shared_ptr<AVStream>(st, [](AVStream* st) { LOG_ON_ERROR2(avcodec_close(st->codec), "[ffmpeg_consumer]"); av_freep(&st->codec); av_freep(&st); }); }