File: encoder.h

package info (click to toggle)
vdr-plugin-markad 4.2.15-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,084 kB
  • sloc: cpp: 22,441; python: 613; makefile: 270; sh: 95
file content (384 lines) | stat: -rw-r--r-- 14,438 bytes parent folder | download | duplicates (2)
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
};