src/zm_ffmpeg.h
Rama master · réplica de lectura del repositorio original.
/*
* ZoneMinder FFMPEG Interface, $Date$, $Revision$
* Copyright (C) 2001-2008 Philip Coombes
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef ZM_FFMPEG_H
#define ZM_FFMPEG_H
#include "zm_config.h"
#include "zm_define.h"
#include "zm_logger.h"
#include <list>
#include <memory>
#include <string>
#include <vector>
extern "C" {
#include <libswresample/swresample.h>
// AVUTIL
#include <libavutil/avassert.h>
#include <libavutil/avutil.h>
#include <libavutil/base64.h>
#include <libavutil/mathematics.h>
#include <libavutil/avstring.h>
#include <libavutil/audio_fifo.h>
#include <libavutil/imgutils.h>
#include <libavutil/opt.h>
#if HAVE_LIBAVUTIL_HWCONTEXT_H
#include <libavutil/hwcontext.h>
#endif
/* LIBAVUTIL_VERSION_CHECK checks for the right version of libav and FFmpeg
* The original source is vlc (in modules/codec/avcodec/avcommon_compat.h)
* a is the major version
* b and c the minor and micro versions of libav
* d and e the minor and micro versions of FFmpeg */
#define LIBAVUTIL_VERSION_CHECK(a, b, c, d, e) \
( (LIBAVUTIL_VERSION_MICRO < 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
(LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
#define _AVPIXELFORMAT AVPixelFormat
// AVCODEC
#include <libavcodec/avcodec.h>
/*
* LIBAVCODEC_VERSION_CHECK checks for the right version of libav and FFmpeg
* The original source is vlc (in modules/codec/avcodec/avcommon_compat.h)
* a is the major version
* b and c the minor and micro versions of libav
* d and e the minor and micro versions of FFmpeg */
#define LIBAVCODEC_VERSION_CHECK(a, b, c, d, e) \
( (LIBAVCODEC_VERSION_MICRO < 100 && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
(LIBAVCODEC_VERSION_MICRO >= 100 && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
#define _AVCODECID AVCodecID
// AVFORMAT
#include <libavformat/avformat.h>
/* LIBAVFORMAT_VERSION_CHECK checks for the right version of libav and FFmpeg
* The original source is vlc (in modules/codec/avcodec/avcommon_compat.h)
* a is the major version
* b and c the minor and micro versions of libav
* d and e the minor and micro versions of FFmpeg */
#define LIBAVFORMAT_VERSION_CHECK(a, b, c, d, e) \
( (LIBAVFORMAT_VERSION_MICRO < 100 && LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
(LIBAVFORMAT_VERSION_MICRO >= 100 && LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
// SWSCALE
#include <libswscale/swscale.h>
/* LIBSWSCALE_VERSION_CHECK checks for the right version of libav and FFmpeg
* a is the major version
* b and c the minor and micro versions of libav
* d and e the minor and micro versions of FFmpeg */
#define LIBSWSCALE_VERSION_CHECK(a, b, c, d, e) \
( (LIBSWSCALE_VERSION_MICRO < 100 && LIBSWSCALE_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
(LIBSWSCALE_VERSION_MICRO >= 100 && LIBSWSCALE_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
}
/* A single function to initialize ffmpeg, to avoid multiple initializations */
void FFMPEGInit();
void FFMPEGDeInit();
enum _AVPIXELFORMAT GetFFMPEGPixelFormat(unsigned int p_colours, unsigned p_subpixelorder);
/*
* Some versions of libav does not contain this definition.
*/
#ifndef AV_ERROR_MAX_STRING_SIZE
#define AV_ERROR_MAX_STRING_SIZE 64
#endif
/*
* C++ friendly version of av_err2str taken from http://libav-users.943685.n4.nabble.com/Libav-user-g-4-7-2-fails-to-compile-av-err2str-td4656417.html.
* Newer g++ versions fail with "error: taking address of temporary array" when using native libav version.
*/
inline static const std::string av_make_error_string(int errnum) {
static char errbuf[AV_ERROR_MAX_STRING_SIZE];
av_strerror(errnum, errbuf, AV_ERROR_MAX_STRING_SIZE);
return (std::string)errbuf;
}
#undef av_err2str
#define av_err2str(errnum) av_make_error_string(errnum).c_str()
#ifndef av_rescale_delta
/**
* Rescale a timestamp while preserving known durations.
*/
int64_t av_rescale_delta(AVRational in_tb, int64_t in_ts, AVRational fs_tb, int duration, int64_t *last, AVRational out_tb);
#endif
#ifndef av_clip64
/**
* Clip a signed 64bit integer value into the amin-amax range.
* @param a value to clip
* @param amin minimum value of the clip range
* @param amax maximum value of the clip range
* @return clipped value
*/
static av_always_inline av_const int64_t av_clip64_c(int64_t a, int64_t amin, int64_t amax) {
if (a < amin) return amin;
else if (a > amax) return amax;
else return a;
}
#define av_clip64 av_clip64_c
#endif
void zm_dump_stream(AVStream *st);
void zm_dump_stream_format(AVFormatContext *ic, int i, int index, int is_output);
void zm_dump_codec(const AVCodecContext *codec);
void zm_dump_codecpar(const AVCodecParameters *par);
#if LIBAVUTIL_VERSION_CHECK(57, 28, 100, 28, 0)
#define zm_dump_frame(frame, text) Debug(1, "%s: format %d %s sample_rate %" PRIu32 " nb_samples %d" \
" layout %" PRIu64 " pts %" PRId64, \
text, \
frame->format, \
av_get_sample_fmt_name((AVSampleFormat)frame->format), \
frame->sample_rate, \
frame->nb_samples, \
frame->ch_layout.u.mask, \
frame->pts \
);
#else
#define zm_dump_frame(frame, text) Debug(1, "%s: format %d %s sample_rate %" PRIu32 " nb_samples %d" \
" layout %" PRIu64 " pts %" PRId64, \
text, \
frame->format, \
av_get_sample_fmt_name((AVSampleFormat)frame->format), \
frame->sample_rate, \
frame->nb_samples, \
frame->channel_layout, \
frame->pts \
);
#endif
#if LIBAVUTIL_VERSION_CHECK(58, 7, 100, 7, 0)
#define zm_dump_video_frame(frame, text) Debug(1, "%s: format %d %s %dx%d linesize:%dx%dx%dx%d data:%p,%p,%p,%p pts: %" PRId64 " keyframe: %d", \
text, \
frame->format, \
av_get_pix_fmt_name((AVPixelFormat)frame->format), \
frame->width, \
frame->height, \
frame->linesize[0], frame->linesize[1], \
frame->linesize[2], frame->linesize[3], \
frame->data[0], frame->data[1], \
frame->data[2], frame->data[3], \
frame->pts, \
frame->flags & AV_FRAME_FLAG_KEY \
);
#else
#define zm_dump_video_frame(frame, text) Debug(1, "%s: format %d %s %dx%d linesize:%dx%d pts: %" PRId64 " keyframe: %d", \
text, \
frame->format, \
av_get_pix_fmt_name((AVPixelFormat)frame->format), \
frame->width, \
frame->height, \
frame->linesize[0], frame->linesize[1], \
frame->pts, \
frame->key_frame \
);
#endif
# define AV_PACKET_DURATION_FMT PRId64
#define CODEC_TYPE(stream) stream->codecpar->codec_type
#define CODEC(stream) stream->codecpar
#ifndef DBG_OFF
# define ZM_DUMP_PACKET(pkt, text) \
if (pkt) { Debug(2, "%s: pts: %" PRId64 ", dts: %" PRId64 \
", size: %d, stream_index: %d, flags: %04x, keyframe: %d pos: %" PRId64 ", duration: %" AV_PACKET_DURATION_FMT, \
text,\
pkt->pts,\
pkt->dts,\
pkt->size,\
pkt->stream_index,\
pkt->flags,\
pkt->flags & AV_PKT_FLAG_KEY,\
pkt->pos,\
pkt->duration); } else { \
Error("Null packet send to ZM_DUMP_PACKET"); \
}
# define ZM_DUMP_STREAM_PACKET(stream, pkt, text) \
if (logDebugging()) { \
double pts_time = static_cast<double>(av_rescale_q(pkt->pts, stream->time_base, AV_TIME_BASE_Q)) / AV_TIME_BASE; \
\
Debug(2, "%s: pts: %" PRId64 " * %u/%u=%f, dts: %" PRId64 \
", size: %d, stream_index: %d, %s flags: %04x, keyframe(%d) pos: %" PRId64", duration: %" AV_PACKET_DURATION_FMT, \
text, \
pkt->pts, \
stream->time_base.num, \
stream->time_base.den, \
pts_time, \
pkt->dts, \
pkt->size, \
pkt->stream_index, \
av_get_media_type_string(CODEC_TYPE(stream)), \
pkt->flags, \
pkt->flags & AV_PKT_FLAG_KEY, \
pkt->pos, \
pkt->duration); \
}
#else
# define ZM_DUMP_PACKET(pkt, text)
# define ZM_DUMP_STREAM_PACKET(stream, pkt, text)
#endif
#define zm_av_packet_unref(packet) av_packet_unref(packet)
#define zm_av_packet_ref(dst, src) av_packet_ref(dst, src)
int check_sample_fmt(const AVCodec *codec, enum AVSampleFormat sample_fmt);
enum AVPixelFormat fix_deprecated_pix_fmt(enum AVPixelFormat );
bool pix_fmt_is_jpeg_range(enum AVPixelFormat );
// Correct swscale's default limited-range assumption when either side of the
// conversion was a full-range JPEG (YUVJ*) format. Call after (re)creating the
// context, passing the ORIGINAL pre-fix_deprecated_pix_fmt formats.
void zm_sws_set_ranges(struct SwsContext *ctx,
enum AVPixelFormat original_src_fmt,
enum AVPixelFormat original_dst_fmt);
bool is_video_stream(const AVStream *);
bool is_audio_stream(const AVStream *);
bool is_video_context(const AVCodec *);
bool is_audio_context(const AVCodec *);
int zm_receive_packet(AVCodecContext *context, AVPacket &packet);
int zm_send_packet_receive_frame(AVCodecContext *context, AVFrame *frame, AVPacket &packet);
int zm_send_frame_receive_packet(AVCodecContext *context, AVFrame *frame, AVPacket &packet);
void zm_packet_copy_rescale_ts(const AVPacket *ipkt, AVPacket *opkt, const AVRational src_tb, const AVRational dst_tb);
int zm_resample_audio(SwrContext *resample_ctx, AVFrame *in_frame, AVFrame *out_frame);
int zm_resample_get_delay(SwrContext *resample_ctx, int time_base);
int zm_add_samples_to_fifo(AVAudioFifo *fifo, AVFrame *frame);
int zm_get_samples_from_fifo(AVAudioFifo *fifo, AVFrame *frame);
struct zm_free_av_packet {
void operator()(AVPacket *pkt) const {
av_packet_free(&pkt);
}
};
using av_packet_ptr = std::unique_ptr<AVPacket, zm_free_av_packet>;
struct av_packet_guard {
av_packet_guard() : packet{nullptr} {
}
explicit av_packet_guard(const av_packet_ptr& p) : packet{p.get()} {
}
explicit av_packet_guard(AVPacket *p) : packet{p} {
}
~av_packet_guard() {
if (packet)
av_packet_unref(packet);
}
void acquire(const av_packet_ptr& p) {
packet = p.get();
}
void acquire(AVPacket *p) {
packet = p;
}
void release() {
packet = nullptr;
}
private:
AVPacket *packet;
};
struct zm_free_av_frame {
void operator()(AVFrame *frame) const {
av_frame_free(&frame);
}
};
using av_frame_ptr = std::unique_ptr<AVFrame, zm_free_av_frame>;
struct CodecData {
const AVCodecID codec_id;
const char *codec_codec;
const char *codec_name;
const enum AVPixelFormat sw_pix_fmt;
const enum AVPixelFormat hw_pix_fmt;
#if HAVE_LIBAVUTIL_HWCONTEXT_H && LIBAVCODEC_VERSION_CHECK(57, 107, 0, 107, 0)
const AVHWDeviceType hwdevice_type;
#endif
const char *hwdevice_default;
const char *options_defaults;
};
std::list<const CodecData*> get_encoder_data(const std::string & wanted_codec, const std::string &wanted_coder) ;
std::list<const CodecData*> get_decoder_data(int wanted_codec, const std::string &wanted_coder) ;
// When none of the preferred decoders in dec_codecs are usable, fall back to
// whatever decoder this ffmpeg build actually provides for codecpar->codec_id.
// Returns an opened AVCodecContext (caller frees with avcodec_free_context) or
// nullptr. When non-null, *codec_out receives the chosen AVCodec.
AVCodecContext *open_fallback_decoder(const AVCodecParameters *codecpar, const AVCodec **codec_out = nullptr);
int setup_hwaccel(AVCodecContext *codec_ctx, const CodecData *codec_data,AVBufferRef * &hw_device_ctx, const std::string &device, int width, int height);
int libjpeg_to_ffmpeg_qv(int libjpeg_quality);
enum AVPixelFormat get_hw_format(AVCodecContext *ctx, const enum AVPixelFormat *pix_fmts);
// The hwaccel device types a DecoderHWAccelName asks for, in the order they
// should be tried.
//
// "auto" means every type this libavutil was built with, so the caller probes
// them and takes the first the decoder supports and whose device opens.
// Anything else is a comma-separated priority list; a single name is the
// one-element case. Unknown names are dropped with a warning rather than
// failing the lot, so one typo in a list does not cost the working entries.
// An empty name yields nothing, which the caller reads as "no hwaccel asked
// for".
std::vector<enum AVHWDeviceType> hwaccel_candidate_types(const std::string &hwaccel_name);
// Open a decoder for a video stream, with hardware acceleration if one was
// asked for and can be had.
//
// hw_pix_fmt is taken by reference because the returned context points at it:
// get_hw_format reads it back through ctx->opaque every time libavcodec picks
// a format. Whatever the caller passes must therefore outlive the context, and
// must not be a temporary or a local that goes out of scope first.
//
// hw_device_ctx receives the device the caller must unref once the context is
// gone. label names the source in error messages. Returns nullptr if no
// decoder could be opened at all.
AVCodecContext *open_video_decoder(
AVStream *stream,
const std::string &decoder_name,
const std::string &hwaccel_name,
const std::string &hwaccel_device,
const std::string &options,
const std::string &label,
AVPixelFormat &hw_pix_fmt,
AVBufferRef *&hw_device_ctx);
#endif // ZM_FFMPEG_H