1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
|
/*
* encoder.h: A program for the Video Disk Recorder
*
* See the README file for copyright information and how to reach the author.
*
*/
#include "debug.h"
#include "tools.h"
#include "index.h"
#include "marks.h"
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavformat/avio.h>
#include <libavutil/file.h>
#include <libavutil/opt.h>
#include <libavfilter/avfilter.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <libswresample/swresample.h>
}
#define VOLUME 2dB
enum {
CUT_MODE_INVALID = -1,
CUT_MODE_KEY = 0,
CUT_MODE_SMART = 1,
CUT_MODE_FULL = 2
};
/**
* libav volume filter class
*/
class cAC3VolumeFilter {
public:
cAC3VolumeFilter();
~cAC3VolumeFilter();
/**
* init libav volume filter
* @param channel_layout audio channel layout
* @param sample_fmt audio sample format
* @param sample_rate samples per second
* @return true if volume filter graph was successful created, false otherwise
*/
#if LIBAVCODEC_VERSION_INT >= ((59<<16)+( 25<<8)+100)
bool Init(const AVChannelLayout channel_layout, const enum AVSampleFormat sample_fmt, const int sample_rate);
#else
bool Init(const uint64_t channel_layout, const enum AVSampleFormat sample_fmt, const int sample_rate);
#endif
/**
* send frame to volume filter graph
* @param avFrame audio frame
* @return true if successful, false otherwise
*/
bool SendFrame(AVFrame *avFrame);
/**
* receive frame from volume filter graph
* @param avFrame audio frame
* @return true if successful, false otherwise
*/
bool GetFrame(AVFrame *avFrame);
private:
AVFilterGraph *filterGraph = nullptr; //!< filter graph
//!<
AVFilterContext *filterSrc = nullptr; //!< filter source
//!<
AVFilterContext *filterSink = nullptr; //!< filter sink
//!<
};
/**
* main encoder class
*/
class cEncoder : protected cTools {
public:
/**
* constructor
* @param decoderParam pointer to decoder
* @param indexParam recording index
* @param recDirParam recording directory
* @param cutModeParam cut mode
* @param bestStreamParam true only encode best video and audio stream
* @param ac3ReEncodeParam true if AC3 re-endcode with volume adjust
*/
explicit cEncoder(cDecoder *decoderParam, cIndex *indexParam, const char* recDirParam, const int cutModeParam, const bool bestStreamParam, const bool ac3ReEncodeParam);
~cEncoder();
/**
* reset encoder state and start write file from begin
* @param passEncoder 0 for only one pass planed, 1 first pass of 2 pass encoding, 2 second pass of 2 pass encoding)
*/
void Reset(const int passEncoder);
/**
* open output file
* @return true if successful, false otherwise
*/
bool OpenFile();
/** cut out video from start PTS to stop PTS
* @param startMark start mark
* @param stopMark stop mark
* @return true if successful, false otherwise
*/
bool CutOut(cMark *startMark, cMark *stopMark);
/**
* close output file
* @return true if successful, false otherwise
*/
bool CloseFile();
private:
/** cut out video from start position to stop position of mark with full re-encode
* @param startMark start mark
* @param stopMark stop mark
* @return true if successful, false otherwise
*/
bool CutFullReEncode(const cMark *startMark, const cMark *stopMark);
/** smart cut video from start PTS to stop PTS of mark
* @param startMark start mark
* @param stopMark stop mark
* @return true if successful, false otherwise
*/
bool CutSmart(cMark *startMark, cMark *stopMark);
/** cut out H.265 video from key packet after start mark to key packet before stop mark
* @param startMark start mark
* @param stopMark stop mark
* @return true if successful, false otherwise
*/
bool CutKeyPacket(const cMark *startMark, cMark *stopMark);
/** check if input file changed an set new decoder context
*/
void CheckInputFileChange();
/** get hwaccel encoder name appropriate hwaccel decoder
* param streamIndexIn input stream index
* @return name of encoder
*/
char *GetEncoderName(const int streamIndexIn);
/** write packet to output file
* @param avpkt pointer to packet
* @param reEncoded true if packet was re-encoded, false otherwise
* @return true if successful, false otherwise
*/
bool WritePacket(AVPacket *avpkt, const bool reEncoded);
/** prepare video frame to encode
* @return pointer to encoded packet
*/
bool EncodeVideoFrame();
/** prepare AC3 audio frame to encode
* @return pointer to encoded packet
*/
bool EncodeAC3Frame();
/** get channel count of AC3 output stream
* @param streamIndex stream index
* @return channel count
*/
int GetAC3ChannelCount(const int streamIndex);
/** send frame to encoder
* @param streamIndexOut output sream index
* @param avFrame frame to encode
*/
bool SendFrameToEncoder(const int streamIndexOut, AVFrame *avFrame);
/** receive packet from encoder
* @param streamIndexOut output stream index
* @return encoded packet, nullptr otherwise
*/
AVPacket *ReceivePacketFromEncoder(const int streamIndexOut);
/**
* init encoder codec
* @param streamIndexIn input stream index
* @param streamIndexOut output stream index
* @param addOutStream if true add output stream will be added to format context
* @param forcePixFmt force this pixel format
* @param verbose if true log codec parameters
* @return true if successful, false otherwise
*/
bool InitEncoderCodec(const unsigned int streamIndexIn, const unsigned int streamIndexOut, const bool addOutStream, AVPixelFormat forcePixFmt, const bool verbose);
/**
* reset decoder and encoder codex context
* have to start with empty decoder end encoder queues
* @return true if successful, false otherwise
*/
bool ResetDecoderEncodeCodec();
/**
* drain decoder and encoder queue
* @param startPTS PTS of start mark
* @param stopPTS PTS of stop mark
* @return true if successful, false otherwise
*/
bool DrainVideoReEncode(const int64_t startPTS, const int64_t stopPTS);
/**
* calculate and set encoder queue PTS/DTS offset for smart re-encode
* @param avpkt current output packet
*/
void SetSmartReEncodeOffset(AVPacket *avpkt);
/**
* change audio encoder channel count
* @param streamIndexIn stream index input stream
* @param streamIndexOut stream index output stream
* @param avCodecCtxIn input stream codec context
* @return true if successful, false otherwise
*/
bool ChangeEncoderCodec(const int streamIndexIn, const int streamIndexOut, AVCodecContext *avCodecCtxIn);
/**
* check statistic data after first pass, ffmpeg assert if something is invalid
* @param max_b_frames number of maximum b-frames
* @return true of valid, false otherwise
*/
bool CheckStats(const int max_b_frames) const;
/**
* get next p-slice after PTS
* used if we have no p-slice index from decoder because of no full decode
* @param pts presentation timestamp
* @param pSlicePTS PTS from key packet number of next p-slice
* @param keyPacketNumberBeforeStop key packet before next stop
* @return key packet number of next p-slice
*/
int GetPSliceKeyPacketNumberAfterPTS(int64_t pts, int64_t *pSlicePTS, const int keyPacketNumberBeforeStop);
cDecoder *decoder = nullptr; //!< decoder
//!<
cDecoder *decoderLocal = nullptr; //!< local decoder, used if we have no p-slice index
//!<
bool useHWaccel = false; //!< encoder use hwaccel (same as decoder)
//!<
cIndex *index = nullptr; //!< index
//!<
cIndex *indexLocal = nullptr; //!< local index, used if we have no p-slice index
//!<
const char *recDir = nullptr; //!< recording directory
//!<
int cutMode = CUT_MODE_INVALID; //!< cut mode
//!<
bool bestStream = false; //!< true if only endcode best video and audio stream
//!<
bool ac3ReEncode = false; //!< true if ac3 re-encode with volume adjust
//!<
int fileNumber = 0; //!< input file number
//!<
bool forceIFrame = false; //!< force next encoded frame to i-frame
//!<
AVFormatContext *avctxIn = nullptr; //!< avformat context for input
//!<
AVFormatContext *avctxOut = nullptr; //!< avformat context for output
//!<
AVCodecContext **codecCtxArrayIn = nullptr; //!< avcodec context for each input stream
//!<
int64_t ptsBefore = 0; //!< presentation timestamp of frame before
//!<
int64_t ptsBeforeCut = INT64_MAX; //!< presentation timestamp of frame before cut mark
//!<
int64_t ptsAfterCut = 0; //!< presentation timestamp of frame after cut mark
//!<
int streamMap[MAXSTREAMS] = {-1}; //!< input stream to output stream map
//!<
int videoInputStreamIndex = -1; //!< video input stream index
//!<
int videoOutputStreamIndex = -1; //!< video output stream index
//!<
int pass = 0; //!< encoding pass
//!<
bool rollover = false; //!< PTS/DTS rollover
//!<
bool firstFrameToEncoder = true; //!< true if we send first frame to encoder
//!<
SwrContext *swrArray[MAXSTREAMS] = {nullptr}; //!< array of libswresample (lswr) for audiosample format conversion
//!<
AVCodecContext *codecCtxArrayOut[MAXSTREAMS] = {nullptr}; //!< avcodec context for each output stream
//!<
AVPixelFormat software_pix_fmt = AV_PIX_FMT_NONE; //!< software pixel format from decoder
//!<
cAC3VolumeFilter *volumeFilterAC3[MAXSTREAMS] = {nullptr}; //!< AC3 volume filter
//!<
/**
* stream PTS/DTS infos
*/
struct sStreamInfo {
int64_t lastInPTS[MAXSTREAMS] = {-1}; //!< pts of last intput packet
//!<
int64_t lastOutPTS[MAXSTREAMS] = {-1}; //!< pts of last output packet
//!<
int64_t lastOutDTS[MAXSTREAMS] = {-1}; //!< dts of last output packet
//!<
int64_t maxPTSofGOP = -1; //!< max PTS of current output video GOP
//!<
} streamInfo; //!< infos of stream PTS/DTS
//!<
enum {
CUT_STATE_NULL = 0,
CUT_STATE_FIRSTPACKET = 1,
CUT_STATE_START = 2,
CUT_STATE_STOP = 3,
};
/**
* cut PTS/DTS infos
*/
struct sCutInfo {
int startPacketNumber = -1; //!< packet number of start position
//!<
int64_t startDTS = 0; //!< DTS timestamp of start position
//!<
int64_t startPTS = 0; //!< PTS timestamp of start position
//!<
int stopPacketNumber = -1; //!< packet number of stop position
//!<
int64_t stopDTS = 0; //!< DTS timestamp of stop position
//!<
int64_t stopPTS = 0; //!< PTS timestamp of stop position
//!<
int64_t offset = 0; //!< current offset from input stream to output stream
//!<
int64_t offsetPTSReEncode = 0; //!< additional PTS offset for re-encoded packets
//!<
int64_t offsetDTSReEncode = 0; //!< additional DTS offset for re-encoded packets
//!<
int64_t offsetDTSReceive = 0; //!< additional DTS offset for re-encoded packets with PTS < DTS (found with h264_nvenc)
//!<
int64_t videoPacketDuration = 0; //!< duration of video packet
//!<
int state = CUT_STATE_FIRSTPACKET; //!< state of smart cut
//!<
} cutInfo; //!< infos of cut positions
//!<
/**
* structure for statistic data for 2 pass encoding
*/
struct sAVstatsIn {
char *data = nullptr; //!< statistic data generated from encoder
//!<
long int size = 0; //!< size of statistic data
//!<
} stats_in; //!< variable for statistic data for 2 pass encoding
//!<
#ifdef DEBUG_CUT_WRITE
int64_t outputKeyPacketPTSbefore[MAXSTREAMS] = {-1};
#endif
#if defined(DEBUG_ENCODER) || defined(DEBUG_PTS_DTS_CUT)
int64_t lastPacketOutDTS[MAXSTREAMS] = {-1}; //!< DTS of last output packet from encoder
#endif
#ifdef DEBUG_PTS_DTS_CUT
int64_t inputKeyPacketPTSbefore[MAXSTREAMS] = {-1};
int64_t lastPacketInPTS[MAXSTREAMS] = {-1}; //!< PTS of last input packet
int64_t lastPacketInDTS[MAXSTREAMS] = {-1}; //!< DTS of last input packet
int64_t lastFrameInPTS[MAXSTREAMS] = {-1}; //!< PTS of last input frame from decoder, send to encoder
int64_t lastFrameInDTS[MAXSTREAMS] = {-1}; //!< DTS of last input frame from decoder, send to encoder
//!<
#endif
};
|