#include "av.hpp" extern "C" { #include #include #include } #include #include #include // Was not yet defined in :( static std::string operator "" s(const char *str, std::size_t len){ return {str, len}; } namespace av { format_context format_open_input(const std::string& filename, AVInputFormat* format, AVDictionary** options){ AVFormatContext * ctx = nullptr; avformat_open_input(&ctx, filename.c_str(), format, options); if(!ctx) throw error("Unable to open input " + filename); // This might be optional, but AFAIK people always do this after opening input avformat_find_stream_info(ctx, options); return {ctx, [](auto x){ avformat_close_input(&x); }}; } format_context format_alloc_context(){ auto ptr = avformat_alloc_context(); if(!ptr) throw error("Could not allocate AVFormatContext"); return {avformat_alloc_context(), &avformat_free_context}; } codec find_encoder(AVCodecID codec_id){ auto ptr = avcodec_find_encoder(codec_id); if(!ptr) throw error("Could not find codec"); return {ptr, nullptr}; } codec_context context_alloc(codec const & codec){ auto ptr = avcodec_alloc_context3(codec.get()); if(!ptr) throw av::error("Could not allocate codec context"); return {ptr, [](auto x){ avcodec_free_context(&x); }}; } codec_context context_from_stream(format_context const & ctx, size_t i){ return {ctx->streams[i]->codec, nullptr}; } open_guard codec_open(codec_context & ctx, codec const & codec, AVDictionary** options){ if(!ctx) throw error("Invalid codec context"); if(!codec) throw error("Invalid codec"); if(avcodec_open2(ctx.get(), codec.get(), options) < 0) throw error("Could not open codec"); return {ctx.get(), [](auto x){ avcodec_close(x); }}; } packet read_frame(format_context& ctx, packet_buffer& p){ auto ret = av_read_frame(ctx.get(), &p); if(ret < 0){ return {nullptr, &av_free_packet}; } else { return {&p, &av_free_packet}; } } frame frame_alloc() { auto ptr = av_frame_alloc(); if(!ptr) throw error("Could not allocate AVFrame"); return {ptr, [](auto x){ av_frame_free(&x); }}; } frame frame_clone(const frame& f){ auto ptr = av_frame_clone(f.get()); if(!ptr) throw error("Could not clone AVFrame"); return {ptr, [](auto x){ av_frame_free(&x); }}; } AVPixelFormat get_format(const frame& f){ return static_cast(f->format); } void crop(frame& f, int left, int top, int width, int height){ f = crop(std::move(f), left, top, width, height); } frame crop(frame&& f, int left, int top, int width, int height){ // if(left + width > f->width || top + height > f->height) throw error("Crop sizes do not match"); auto ptr = reinterpret_cast(f.get()); auto ret = av_picture_crop(ptr, ptr, av::get_format(f), top, left); if(ret < 0) throw error("Could not crop"); f->width = width; f->height = height; return std::move(f); } frame crop(const frame& f, int left, int top, int width, int height){ return crop(frame_clone(f), left, top, width, height); } bool codec_decode_video(codec_context& ctx, frame& f, const packet& pkt){ int finished = 0; int ret = avcodec_decode_video2(ctx.get(), f.get(), &finished, pkt.get()); if (ret < 0) { throw std::runtime_error("Error ["s + std::to_string(ret) + "] while decoding frame: "s + strerror(AVERROR(ret))); } return finished; } void codec_decode_video_remaining(codec_context& ctx, frame& f){ AVPacket pkt{0, 0, 0, 0, 0}; // data and size should be 0 int finished = 0; int ret = avcodec_decode_video2(ctx.get(), f.get(), &finished, &pkt); if (ret < 0) { throw std::runtime_error("Error ["s + std::to_string(ret) + "] while decoding remaining frame: "s + strerror(AVERROR(ret))); } } }