stuff
This commit is contained in:
commit
33ed83bb93
4 changed files with 345 additions and 0 deletions
73
.gitignore
vendored
Normal file
73
.gitignore
vendored
Normal file
|
@ -0,0 +1,73 @@
|
|||
# This file is used to ignore files which are generated
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
*~
|
||||
*.autosave
|
||||
*.a
|
||||
*.core
|
||||
*.moc
|
||||
*.o
|
||||
*.obj
|
||||
*.orig
|
||||
*.rej
|
||||
*.so
|
||||
*.so.*
|
||||
*_pch.h.cpp
|
||||
*_resource.rc
|
||||
*.qm
|
||||
.#*
|
||||
*.*#
|
||||
core
|
||||
!core/
|
||||
tags
|
||||
.DS_Store
|
||||
.directory
|
||||
*.debug
|
||||
Makefile*
|
||||
*.prl
|
||||
*.app
|
||||
moc_*.cpp
|
||||
ui_*.h
|
||||
qrc_*.cpp
|
||||
Thumbs.db
|
||||
*.res
|
||||
*.rc
|
||||
/.qmake.cache
|
||||
/.qmake.stash
|
||||
|
||||
# qtcreator generated files
|
||||
*.pro.user*
|
||||
|
||||
# xemacs temporary files
|
||||
*.flc
|
||||
|
||||
# Vim temporary files
|
||||
.*.swp
|
||||
|
||||
# Visual Studio generated files
|
||||
*.ib_pdb_index
|
||||
*.idb
|
||||
*.ilk
|
||||
*.pdb
|
||||
*.sln
|
||||
*.suo
|
||||
*.vcproj
|
||||
*vcproj.*.*.user
|
||||
*.ncb
|
||||
*.sdf
|
||||
*.opensdf
|
||||
*.vcxproj
|
||||
*vcxproj.*
|
||||
|
||||
# MinGW generated files
|
||||
*.Debug
|
||||
*.Release
|
||||
|
||||
# Python byte code
|
||||
*.pyc
|
||||
|
||||
# Binaries
|
||||
# --------
|
||||
*.dll
|
||||
*.exe
|
||||
|
29
CMakeLists.txt
Normal file
29
CMakeLists.txt
Normal file
|
@ -0,0 +1,29 @@
|
|||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(ffmpegpp LANGUAGES CXX)
|
||||
|
||||
# set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
# set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
add_library(ffmpegpp STATIC
|
||||
ffmpegpp.cpp
|
||||
ffmpegpp.hpp
|
||||
)
|
||||
|
||||
find_path(AVUTIL_INCLUDE_DIR libavutil/avutil.h)
|
||||
find_library(AVUTIL_LIBRARY avutil)
|
||||
|
||||
find_path(SWSCALE_INCLUDE_DIR libswscale/swscale.h)
|
||||
find_library(SWSCALE_LIBRARY swscale)
|
||||
|
||||
find_path(AVCODEC_INCLUDE_DIR libavcodec/avcodec.h)
|
||||
find_library(AVCODEC_LIBRARY avcodec)
|
||||
|
||||
find_path(AVFORMAT_INCLUDE_DIR libavformat/avformat.h)
|
||||
find_library(AVFORMAT_LIBRARY avformat)
|
||||
|
||||
target_include_directories(ffmpegpp PRIVATE ${AVUTIL_INCLUDE_DIR} ${SWSCALE_INCLUDE_DIR} ${AVCODEC_INCLUDE_DIR} ${AVFORMAT_INCLUDE_DIR})
|
||||
target_link_libraries(ffmpegpp ${AVUTIL_LIBRARY} ${SWSCALE_LIBRARY} ${AVCODEC_LIBRARY} ${AVFORMAT_LIBRARY})
|
||||
|
||||
target_compile_definitions(ffmpegpp PRIVATE)
|
165
ffmpegpp.cpp
Normal file
165
ffmpegpp.cpp
Normal file
|
@ -0,0 +1,165 @@
|
|||
#include "ffmpegpp.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libswscale/swscale.h>
|
||||
}
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
namespace av {
|
||||
|
||||
codec find_encoder(codec_id id) {
|
||||
auto ptr = avcodec_find_encoder(static_cast<AVCodecID>(id));
|
||||
if(!ptr) throw error("Could not find codec");
|
||||
return {ptr};
|
||||
}
|
||||
|
||||
|
||||
frame::frame()
|
||||
: ptr(av_frame_alloc()) {}
|
||||
|
||||
void frame::deleter::operator()(AVFrame *p) {
|
||||
av_frame_free(&p);
|
||||
}
|
||||
|
||||
|
||||
packet::packet()
|
||||
: ptr(av_packet_alloc()) {}
|
||||
|
||||
void packet::deleter::operator()(AVPacket *p) {
|
||||
av_packet_free(&p);
|
||||
}
|
||||
|
||||
|
||||
format_context::format_context() : ptr(avformat_alloc_context()) {}
|
||||
|
||||
format_context::format_context(const std::string &url) {
|
||||
AVFormatContext * p = nullptr;
|
||||
avformat_open_input(&p, url.c_str(), nullptr, nullptr);
|
||||
// This might be optional, but AFAIK people always do this after opening input
|
||||
avformat_find_stream_info(p, nullptr);
|
||||
ptr.reset(p);
|
||||
}
|
||||
|
||||
codec_context format_context::context_from_stream(int i) const {
|
||||
(void)ptr->streams[i]->codecpar;
|
||||
return {};
|
||||
}
|
||||
|
||||
void format_context::deleter::operator()(AVFormatContext *p) {
|
||||
avformat_free_context(p);
|
||||
}
|
||||
|
||||
|
||||
codec_context::codec_context()
|
||||
: ptr(avcodec_alloc_context3(nullptr)) {}
|
||||
|
||||
codec_context::codec_context(codec const & c)
|
||||
: ptr(avcodec_alloc_context3(c.ptr)) {}
|
||||
|
||||
void codec_context::open(const codec &c) {
|
||||
if (avcodec_open2(ptr.get(), c.ptr, nullptr) < 0) {
|
||||
throw error("Open coded failed");
|
||||
}
|
||||
}
|
||||
|
||||
void codec_context::send_frame(const frame & f) {
|
||||
if (avcodec_send_frame(ptr.get(), f.ptr.get()) < 0) {
|
||||
throw error("Could not send frame");
|
||||
}
|
||||
}
|
||||
|
||||
int codec_context::receive_packet(packet & p) {
|
||||
auto ret = avcodec_receive_packet(ptr.get(), p.ptr.get());
|
||||
if (ret >= 0 || ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
|
||||
return ret;
|
||||
if (ret < 0)
|
||||
throw error("Could not receive packet");
|
||||
return ret;
|
||||
}
|
||||
|
||||
void codec_context::send_packet(const packet & p) {
|
||||
if (avcodec_send_packet(ptr.get(), p.ptr.get()) < 0)
|
||||
throw error("send packet error");
|
||||
}
|
||||
|
||||
int codec_context::receive_frame(frame & f) {
|
||||
auto ret = avcodec_receive_frame(ptr.get(), f.ptr.get());
|
||||
if (ret >= 0 || ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
|
||||
return ret;
|
||||
if (ret < 0)
|
||||
throw error("could not receive frame");
|
||||
return ret;
|
||||
}
|
||||
|
||||
void codec_context::set_parameters(const AVCodecParameters * par) {
|
||||
if (avcodec_parameters_to_context(ptr.get(), par) < 0)
|
||||
throw error("error with parameters");
|
||||
}
|
||||
|
||||
void codec_context::deleter::operator()(AVCodecContext *p) {
|
||||
avcodec_free_context(&p);
|
||||
}
|
||||
|
||||
|
||||
void encode_as_jpg(const frame & frame, const std::string & filename){
|
||||
const auto codec = av::find_encoder(AV_CODEC_ID_MJPEG);
|
||||
|
||||
codec_context codec_ctx(codec);
|
||||
codec_ctx.ptr->pix_fmt = static_cast<AVPixelFormat>(frame.ptr->format);
|
||||
codec_ctx.ptr->width = frame.ptr->width;
|
||||
codec_ctx.ptr->height = frame.ptr->height;
|
||||
codec_ctx.ptr->time_base = av_make_q(1, 1);
|
||||
|
||||
codec_ctx.open(codec);
|
||||
codec_ctx.send_frame(frame);
|
||||
|
||||
packet p;
|
||||
std::ofstream file(filename);
|
||||
|
||||
int ret = 0;
|
||||
while (ret >= 0) {
|
||||
ret = codec_ctx.receive_packet(p);
|
||||
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
|
||||
return;
|
||||
|
||||
file.write(reinterpret_cast<char*>(p.ptr->data), p.ptr->size);
|
||||
av_packet_unref(p.ptr.get());
|
||||
}
|
||||
}
|
||||
|
||||
frame open_image(std::string const & filename){
|
||||
format_context format_ctx(filename);
|
||||
|
||||
AVCodec * ptr;
|
||||
const auto idx = av_find_best_stream(format_ctx.ptr.get(), AVMEDIA_TYPE_VIDEO, -1, -1, &ptr, 0);
|
||||
if (idx < 0)
|
||||
std::clog << "error finding best stream" << std::endl;
|
||||
|
||||
const auto st = format_ctx.ptr->streams[idx];
|
||||
const auto dc = avcodec_find_decoder(st->codecpar->codec_id);
|
||||
|
||||
if (!dc)
|
||||
std::clog << "error finding decoder" << std::endl;
|
||||
|
||||
codec_context codec_ctx({dc});
|
||||
codec_ctx.set_parameters(st->codecpar);
|
||||
codec_ctx.open({dc});
|
||||
|
||||
packet p;
|
||||
|
||||
// TODO: parse
|
||||
av_parser_parse2();
|
||||
|
||||
codec_ctx.send_packet(p);
|
||||
|
||||
frame f;
|
||||
codec_ctx.receive_frame(f);
|
||||
return f;
|
||||
}
|
||||
|
||||
}
|
78
ffmpegpp.hpp
Normal file
78
ffmpegpp.hpp
Normal file
|
@ -0,0 +1,78 @@
|
|||
#pragma once
|
||||
|
||||
extern "C" {
|
||||
typedef struct AVCodec AVCodec;
|
||||
typedef struct AVCodecContext AVCodecContext;
|
||||
typedef struct AVCodecParameters AVCodecParameters;
|
||||
typedef struct AVFrame AVFrame;
|
||||
typedef struct AVFormatContext AVFormatContext;
|
||||
typedef struct AVPacket AVPacket;
|
||||
}
|
||||
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
|
||||
|
||||
namespace av {
|
||||
struct error : public std::runtime_error {
|
||||
using runtime_error::runtime_error;
|
||||
};
|
||||
|
||||
// an enum in reality
|
||||
using codec_id = int;
|
||||
|
||||
// Has no ownership
|
||||
struct codec {
|
||||
AVCodec * ptr;
|
||||
};
|
||||
|
||||
codec find_encoder(codec_id id);
|
||||
|
||||
struct frame {
|
||||
frame();
|
||||
|
||||
struct deleter { void operator()(AVFrame * p); };
|
||||
std::unique_ptr<AVFrame, deleter> ptr;
|
||||
};
|
||||
|
||||
// TODO: are ref counted... How to do?
|
||||
struct packet {
|
||||
packet();
|
||||
|
||||
struct deleter { void operator()(AVPacket * p); };
|
||||
std::unique_ptr<AVPacket, deleter> ptr;
|
||||
};
|
||||
|
||||
struct codec_context {
|
||||
codec_context();
|
||||
codec_context(codec const & c);
|
||||
|
||||
// If constructed with codec, it must be the same
|
||||
// need not be closed
|
||||
void open(codec const & c);
|
||||
|
||||
void send_frame(frame const & f);
|
||||
int receive_packet(packet & p);
|
||||
|
||||
void send_packet(packet const & p);
|
||||
int receive_frame(frame & f);
|
||||
|
||||
void set_parameters(const AVCodecParameters *par);
|
||||
|
||||
struct deleter { void operator()(AVCodecContext * p); };
|
||||
std::unique_ptr<AVCodecContext, deleter> ptr;
|
||||
};
|
||||
|
||||
struct format_context {
|
||||
format_context();
|
||||
format_context(const std::string & url);
|
||||
|
||||
codec_context context_from_stream(int i) const;
|
||||
|
||||
struct deleter { void operator()(AVFormatContext * p); };
|
||||
std::unique_ptr<AVFormatContext, deleter> ptr;
|
||||
};
|
||||
|
||||
void encode_as_jpg(const frame & frame, const std::string & filename);
|
||||
frame open_image(const std::string & filename);
|
||||
}
|
Reference in a new issue