@0biwank/screen-capture 2.0.1 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,484 +0,0 @@
1
- #include "HLSMuxer/AudioEncoder.h"
2
- #include <iostream>
3
- #include <sstream>
4
- #include <iomanip>
5
- #include <algorithm>
6
-
7
- extern "C"
8
- {
9
- #include <libavcodec/avcodec.h>
10
- #include <libavformat/avformat.h>
11
- #include <libswresample/swresample.h>
12
- #include <libavutil/opt.h>
13
- #include <libavutil/channel_layout.h>
14
- #include <libavutil/audio_fifo.h>
15
- }
16
-
17
- AudioEncoder::AudioEncoder()
18
- {
19
- m_codecContext = nullptr;
20
- m_codec = nullptr;
21
- m_frame = nullptr;
22
- m_swrContext = nullptr;
23
- m_formatContext = nullptr;
24
- m_stream = nullptr;
25
- m_audioFifo = nullptr;
26
-
27
- m_sampleCount = 0;
28
- m_pts = 0;
29
- m_nextInputPts = 0;
30
- currentChunkIndex = 0;
31
- m_initialized = false;
32
- }
33
-
34
- AudioEncoder::~AudioEncoder()
35
- {
36
- cleanup();
37
- while (!m_packetQueue.empty())
38
- {
39
- AVPacket *pkt = m_packetQueue.front();
40
- m_packetQueue.pop();
41
- av_packet_free(&pkt);
42
- }
43
- }
44
-
45
- bool AudioEncoder::initialize(int sampleRate, int channels, int bitrate, OutputMode mode)
46
- {
47
- m_codec = avcodec_find_encoder(AV_CODEC_ID_AAC);
48
- if (!m_codec)
49
- {
50
- std::cerr << "AAC encoder not found!" << std::endl;
51
- return false;
52
- }
53
- m_codecContext = avcodec_alloc_context3(m_codec);
54
- if (!m_codecContext)
55
- {
56
- std::cerr << "Could not allocate codec context" << std::endl;
57
- return false;
58
- }
59
- m_codecContext->bit_rate = bitrate;
60
- m_codecContext->sample_fmt = AV_SAMPLE_FMT_FLTP;
61
- m_codecContext->sample_rate = sampleRate;
62
- av_channel_layout_default(&m_codecContext->ch_layout, channels);
63
-
64
- m_sampleRate = sampleRate;
65
- m_channels = channels;
66
- m_bitrate = bitrate;
67
- this->mode = mode;
68
-
69
- int ret = avcodec_open2(m_codecContext, m_codec, nullptr);
70
- if (ret < 0)
71
- {
72
- std::cerr << "Could not open codec" << std::endl;
73
- cleanup();
74
- return false;
75
- }
76
- // Set a sensible time base for audio frames (pts in samples)
77
- m_codecContext->time_base = AVRational{1, sampleRate};
78
- m_swrContext = swr_alloc();
79
- if (!m_swrContext)
80
- {
81
- std::cerr << "Could not allocate SWR context" << std::endl;
82
- cleanup();
83
- return false;
84
- }
85
-
86
- // Set input and output parameters
87
- av_opt_set_chlayout(m_swrContext, "in_chlayout", &m_codecContext->ch_layout, 0);
88
- av_opt_set_int(m_swrContext, "in_sample_rate", sampleRate, 0);
89
- av_opt_set_sample_fmt(m_swrContext, "in_sample_fmt", AV_SAMPLE_FMT_S16, 0);
90
-
91
- av_opt_set_chlayout(m_swrContext, "out_chlayout", &m_codecContext->ch_layout, 0);
92
- av_opt_set_int(m_swrContext, "out_sample_rate", sampleRate, 0);
93
- av_opt_set_sample_fmt(m_swrContext, "out_sample_fmt", m_codecContext->sample_fmt, 0);
94
-
95
- ret = swr_init(m_swrContext);
96
- if (ret < 0)
97
- {
98
- std::cerr << "Could not initialize SWR context" << std::endl;
99
- cleanup();
100
- return false;
101
- }
102
- m_frame = av_frame_alloc();
103
- if (!m_frame)
104
- {
105
- cleanup();
106
- return false;
107
- }
108
- m_frame->format = m_codecContext->sample_fmt;
109
- m_frame->ch_layout = m_codecContext->ch_layout;
110
- m_frame->sample_rate = m_codecContext->sample_rate;
111
- m_frame->nb_samples = m_codecContext->frame_size;
112
-
113
- ret = av_frame_get_buffer(m_frame, 0);
114
- if (ret < 0)
115
- {
116
- std::cerr << "Could not allocate frame buffer" << std::endl;
117
- cleanup();
118
- return false;
119
- }
120
-
121
- m_audioFifo = av_audio_fifo_alloc(
122
- m_codecContext->sample_fmt,
123
- m_codecContext->ch_layout.nb_channels,
124
- m_codecContext->frame_size * 2);
125
-
126
- if (!m_audioFifo)
127
- {
128
- std::cerr << "Could not allocate audio FIFO" << std::endl;
129
- cleanup();
130
- return false;
131
- }
132
-
133
- if (mode == OutputMode::STANDALONE)
134
- {
135
- avformat_alloc_output_context2(&m_formatContext, nullptr, "mpegts", nullptr);
136
- if (!m_formatContext)
137
- {
138
- std::cerr << "Could not create output context for TS" << std::endl;
139
- cleanup();
140
- return false;
141
- }
142
-
143
- m_stream = avformat_new_stream(m_formatContext, nullptr);
144
- if (!m_stream)
145
- {
146
- std::cerr << "Could not create audio stream" << std::endl;
147
- cleanup();
148
- return false;
149
- }
150
-
151
- avcodec_parameters_from_context(m_stream->codecpar, m_codecContext);
152
- // Use the same time base as the codec context for simplicity
153
- m_stream->time_base = m_codecContext->time_base;
154
- targetChunkDuration = 4.0;
155
- currentSegmentStartPts = 0;
156
- currentSegmentDuration = 0.0;
157
- }
158
- m_initialized = true;
159
- return true;
160
- }
161
-
162
- bool AudioEncoder::encodeSamples(const uint8_t *samples, int numSamples, int64_t pts)
163
- {
164
- if (!m_initialized)
165
- {
166
- std::cerr << "Encoder is not initialized!" << std::endl;
167
- return false;
168
- }
169
- int samplesToEncode = numSamples;
170
- const uint8_t* samplesToUse = samples;
171
- if (pts >= 0) {
172
- if (pts < m_nextInputPts) {
173
- const int64_t overlap = m_nextInputPts - pts;
174
- if (overlap >= numSamples) {
175
- return true;
176
- }
177
- const int bytesPerFrame = m_channels * static_cast<int>(sizeof(int16_t));
178
- samplesToUse = samples + (overlap * bytesPerFrame);
179
- samplesToEncode = static_cast<int>(numSamples - overlap);
180
- pts = m_nextInputPts;
181
- }
182
-
183
- if (pts > m_nextInputPts + m_codecContext->frame_size &&
184
- av_audio_fifo_size(m_audioFifo) > 0)
185
- {
186
- av_audio_fifo_reset(m_audioFifo);
187
- m_sampleCount = pts;
188
- } else if (av_audio_fifo_size(m_audioFifo) == 0) {
189
- m_sampleCount = pts;
190
- }
191
- }
192
-
193
- if (samplesToEncode <= 0) {
194
- return true;
195
- }
196
-
197
- uint8_t **resampledData = nullptr;
198
- int linesize;
199
- int resampledSamples = av_samples_alloc_array_and_samples(
200
- &resampledData,
201
- &linesize,
202
- m_codecContext->ch_layout.nb_channels,
203
- samplesToEncode,
204
- m_codecContext->sample_fmt,
205
- 0);
206
- if (resampledSamples < 0)
207
- {
208
- std::cerr << "Could not allocate resampled buffer" << std::endl;
209
- return false;
210
- }
211
- const uint8_t *inputData[1] = {samplesToUse};
212
- int outputSamples = swr_convert(
213
- m_swrContext,
214
- resampledData,
215
- samplesToEncode,
216
- inputData,
217
- samplesToEncode);
218
-
219
- if (outputSamples < 0)
220
- {
221
- av_freep(&resampledData[0]);
222
- av_freep(&resampledData);
223
- return false;
224
- }
225
-
226
- av_audio_fifo_write(m_audioFifo, (void **)resampledData, outputSamples);
227
- while (av_audio_fifo_size(m_audioFifo) >= m_codecContext->frame_size)
228
- {
229
- av_frame_make_writable(m_frame);
230
- av_audio_fifo_read(m_audioFifo, (void **)m_frame->data, m_codecContext->frame_size);
231
- m_frame->pts = m_sampleCount;
232
- m_pts = m_frame->pts;
233
-
234
- int ret = avcodec_send_frame(m_codecContext, m_frame);
235
- if (ret < 0)
236
- {
237
- std::cerr << "Error sending frame to encoder" << std::endl;
238
- av_freep(&resampledData[0]);
239
- av_freep(&resampledData);
240
- return false;
241
- }
242
-
243
- while (ret >= 0)
244
- {
245
- AVPacket *packet = av_packet_alloc();
246
- ret = avcodec_receive_packet(m_codecContext, packet);
247
-
248
- if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
249
- {
250
- av_packet_free(&packet);
251
- break;
252
- }
253
-
254
- if (mode == OutputMode::MUXED)
255
- {
256
- m_packetQueue.push(packet);
257
- }
258
- else if (mode == OutputMode::STANDALONE)
259
- {
260
- if (shouldStartNewChunk() || currentChunkIndex == 0)
261
- {
262
- // Close current chunk if exists
263
- if (m_formatContext && m_formatContext->pb)
264
- {
265
- av_write_trailer(m_formatContext);
266
- avio_closep(&m_formatContext->pb);
267
-
268
- currentSegmentDuration = (m_pts - currentSegmentStartPts) * av_q2d(m_codecContext->time_base);
269
- }
270
-
271
- // Generate filename for new chunk
272
- std::ostringstream filename;
273
- filename << "audio_chunk" << std::setfill('0') << std::setw(3) << currentChunkIndex << ".ts";
274
- std::string chunkFilename = filename.str();
275
- m_chunkFileNames.push_back(chunkFilename);
276
-
277
- // Open new chunk file
278
- if (avio_open(&m_formatContext->pb, chunkFilename.c_str(), AVIO_FLAG_WRITE) < 0)
279
- {
280
- std::cerr << "Could not open output file: " << chunkFilename << std::endl;
281
- av_packet_free(&packet);
282
- break;
283
- }
284
-
285
- // Write header for new chunk
286
- if (avformat_write_header(m_formatContext, nullptr) < 0)
287
- {
288
- std::cerr << "Could not write header for new chunk" << std::endl;
289
- av_packet_free(&packet);
290
- break;
291
- }
292
-
293
- currentChunkIndex++;
294
- currentSegmentStartPts = m_pts;
295
- }
296
-
297
- // Write packet to current chunk
298
- packet->stream_index = m_stream->index;
299
- av_packet_rescale_ts(packet, m_codecContext->time_base, m_stream->time_base);
300
- if (av_interleaved_write_frame(m_formatContext, packet) < 0)
301
- {
302
- std::cerr << "Failed to write audio packet" << std::endl;
303
- }
304
- av_packet_free(&packet);
305
- }
306
- }
307
- m_sampleCount += m_codecContext->frame_size;
308
- }
309
- if (pts >= 0) {
310
- m_nextInputPts = std::max<int64_t>(m_nextInputPts, pts + samplesToEncode);
311
- } else {
312
- m_nextInputPts = std::max<int64_t>(m_nextInputPts, m_sampleCount);
313
- }
314
- av_freep(&resampledData[0]);
315
- av_freep(&resampledData);
316
-
317
- return true;
318
- }
319
-
320
- bool AudioEncoder::hasPackets() const
321
- {
322
- return !m_packetQueue.empty();
323
- }
324
-
325
- AVPacket *AudioEncoder::getNextPacket()
326
- {
327
- if (m_packetQueue.empty())
328
- {
329
- return nullptr;
330
- }
331
- AVPacket *packet = m_packetQueue.front();
332
- m_packetQueue.pop();
333
- return packet;
334
- }
335
-
336
- bool AudioEncoder::shouldStartNewChunk() const
337
- {
338
- double duration = (m_pts - currentSegmentStartPts) * av_q2d(m_codecContext->time_base);
339
- return duration >= targetChunkDuration;
340
- }
341
-
342
- void AudioEncoder::forceNewChunk()
343
- {
344
- }
345
-
346
- void AudioEncoder::cleanup()
347
- {
348
- // Flush encoder to get remaining packets
349
- if (m_initialized && m_codecContext)
350
- {
351
- flushEncoder();
352
-
353
- // Finalize last segment if in STANDALONE mode
354
- if (mode == OutputMode::STANDALONE && m_formatContext && m_formatContext->pb)
355
- {
356
- // Update duration for the final segment
357
- currentSegmentDuration = (m_pts - currentSegmentStartPts) * av_q2d(m_codecContext->time_base);
358
-
359
- av_write_trailer(m_formatContext);
360
- avio_closep(&m_formatContext->pb);
361
- }
362
- }
363
-
364
- if (mode != OutputMode::MUXED)
365
- {
366
- while (!m_packetQueue.empty())
367
- {
368
- AVPacket *pkt = m_packetQueue.front();
369
- m_packetQueue.pop();
370
- av_packet_free(&pkt);
371
- }
372
- }
373
- if (m_audioFifo)
374
- {
375
- av_audio_fifo_free(m_audioFifo);
376
- m_audioFifo = nullptr;
377
- }
378
- if (m_frame)
379
- {
380
- av_frame_free(&m_frame);
381
- m_frame = nullptr;
382
- }
383
- if (m_swrContext)
384
- {
385
- swr_free(&m_swrContext);
386
- m_swrContext = nullptr;
387
- }
388
- if (m_formatContext)
389
- {
390
- if (m_formatContext->pb)
391
- {
392
- // Should already be closed above, but ensure it's closed
393
- avio_closep(&m_formatContext->pb);
394
- }
395
- avformat_free_context(m_formatContext);
396
- m_formatContext = nullptr;
397
- }
398
- if (m_codecContext)
399
- {
400
- avcodec_free_context(&m_codecContext);
401
- m_codecContext = nullptr;
402
- }
403
- m_initialized = false;
404
- }
405
-
406
- std::string AudioEncoder::generatePlaylist() const
407
- {
408
- if (mode != OutputMode::STANDALONE || m_chunkFileNames.empty())
409
- {
410
- return "";
411
- }
412
-
413
- std::string playlist = "#EXTM3U\n";
414
- playlist += "#EXT-X-VERSION:3\n";
415
- playlist += "#EXT-X-TARGETDURATION:" + std::to_string(static_cast<int>(targetChunkDuration + 1)) + "\n";
416
- playlist += "#EXT-X-MEDIA-SEQUENCE:0\n";
417
-
418
- for (size_t i = 0; i < m_chunkFileNames.size(); ++i)
419
- {
420
- double duration = targetChunkDuration;
421
- if (i == m_chunkFileNames.size() - 1 && currentSegmentDuration > 0)
422
- {
423
- duration = currentSegmentDuration; // Last segment might be shorter
424
- }
425
-
426
- playlist += "#EXTINF:" + std::to_string(duration) + ",\n";
427
- playlist += m_chunkFileNames[i] + "\n";
428
- }
429
-
430
- // Add end marker if encoding is complete
431
- if (!m_initialized)
432
- {
433
- playlist += "#EXT-X-ENDLIST\n";
434
- }
435
-
436
- return playlist;
437
- }
438
-
439
- // Drain encoder and handle remaining packets based on mode
440
- void AudioEncoder::flushEncoder()
441
- {
442
- if (!m_codecContext)
443
- return;
444
-
445
- // Send NULL frame to signal flushing
446
- int ret = avcodec_send_frame(m_codecContext, nullptr);
447
- if (ret < 0 && ret != AVERROR_EOF)
448
- return;
449
-
450
- while (true)
451
- {
452
- AVPacket *packet = av_packet_alloc();
453
- ret = avcodec_receive_packet(m_codecContext, packet);
454
- if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
455
- {
456
- av_packet_free(&packet);
457
- break;
458
- }
459
- if (ret < 0)
460
- {
461
- av_packet_free(&packet);
462
- break;
463
- }
464
-
465
- if (mode == OutputMode::MUXED)
466
- {
467
- m_packetQueue.push(packet);
468
- }
469
- else if (mode == OutputMode::STANDALONE && m_formatContext && m_formatContext->pb)
470
- {
471
- packet->stream_index = m_stream->index;
472
- av_packet_rescale_ts(packet, m_codecContext->time_base, m_stream->time_base);
473
- if (av_interleaved_write_frame(m_formatContext, packet) < 0)
474
- {
475
- std::cerr << "Failed to write flushed audio packet" << std::endl;
476
- }
477
- av_packet_free(&packet);
478
- }
479
- else
480
- {
481
- av_packet_free(&packet);
482
- }
483
- }
484
- }