10#include <QtCore/qloggingcategory.h>
23 qCDebug(qLcFFmpegAudioEncoder) <<
"AudioEncoder" <<
settings.audioCodec();
26 Q_ASSERT(avformat_query_codec(recordingEngine.avFormatContext()->oformat, codecID,
27 FF_COMPLIANCE_NORMAL));
36 qCDebug(qLcFFmpegAudioEncoder) <<
"found audio codec" << m_avCodec->name;
40 m_stream = avformat_new_stream(recordingEngine.avFormatContext(),
nullptr);
41 m_stream->id = recordingEngine.avFormatContext()->nb_streams - 1;
42 m_stream->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
43 m_stream->codecpar->codec_id = codecID;
44#if QT_FFMPEG_OLD_CHANNEL_LAYOUT
45 m_stream->codecpar->channel_layout =
47 m_stream->codecpar->channels =
qPopulationCount(m_stream->codecpar->channel_layout);
49 m_stream->codecpar->ch_layout =
52 const auto sampleRate =
53 adjustSampleRate(m_avCodec->supported_samplerates, requestedAudioFormat.sampleRate);
55 m_stream->codecpar->sample_rate = sampleRate;
56 m_stream->codecpar->frame_size = 1024;
57 m_stream->codecpar->format =
60 m_stream->time_base = AVRational{ 1, sampleRate };
62 qCDebug(qLcFFmpegAudioEncoder) <<
"set stream time_base" << m_stream->time_base.num <<
"/"
63 << m_stream->time_base.den;
66void AudioEncoder::open()
68 m_codecContext.reset(avcodec_alloc_context3(m_avCodec));
70 if (m_stream->time_base.num != 1 || m_stream->time_base.den != m_format.
sampleRate()) {
71 qCDebug(qLcFFmpegAudioEncoder) <<
"Most likely, av_format_write_header changed time base from"
73 << m_stream->time_base;
76 m_codecContext->time_base = m_stream->time_base;
78 avcodec_parameters_to_context(m_codecContext.get(), m_stream->codecpar);
80 AVDictionaryHolder opts;
84 int res = avcodec_open2(m_codecContext.get(), m_avCodec, opts);
85 qCDebug(qLcFFmpegAudioEncoder) <<
"audio codec opened" <<
res;
86 qCDebug(qLcFFmpegAudioEncoder) <<
"audio codec params: fmt=" << m_codecContext->sample_fmt
87 <<
"rate=" << m_codecContext->sample_rate;
89 const AVAudioFormat requestedAudioFormat(m_format);
90 const AVAudioFormat codecAudioFormat(m_codecContext.get());
92 if (requestedAudioFormat != codecAudioFormat)
99 const std::chrono::microseconds bufferDuration(
buffer.duration());
107 m_audioBufferQueue.push(
buffer);
108 m_queueDuration += bufferDuration;
118 m_queueDuration -= std::chrono::microseconds(
result.duration());
127 if (
auto input = qobject_cast<QFFmpegAudioInput *>(
source()))
128 input->setFrameSize(m_codecContext->frame_size);
130 qCDebug(qLcFFmpegAudioEncoder) <<
"AudioEncoder::init started audio device thread.";
135 while (!m_audioBufferQueue.empty())
137 while (avcodec_send_frame(m_codecContext.get(),
nullptr) == AVERROR(EAGAIN))
144 return !m_audioBufferQueue.empty();
147void AudioEncoder::retrievePackets()
151 int ret = avcodec_receive_packet(m_codecContext.get(), packet.get());
153 if (
ret != AVERROR(EOF))
155 if (
ret != AVERROR(EAGAIN)) {
157 av_strerror(
ret, errStr, 1024);
158 qCDebug(qLcFFmpegAudioEncoder) <<
"receive packet" <<
ret << errStr;
165 packet->stream_index = m_stream->id;
176 if (
buffer.format() != m_format) {
178 qWarning() <<
"Get invalid audio format:" <<
buffer.format() <<
", expected:" << m_format;
187 frame->format = m_codecContext->sample_fmt;
188#if QT_FFMPEG_OLD_CHANNEL_LAYOUT
189 frame->channel_layout = m_codecContext->channel_layout;
190 frame->channels = m_codecContext->channels;
192 frame->ch_layout = m_codecContext->ch_layout;
194 frame->sample_rate = m_codecContext->sample_rate;
196 if (
frame->nb_samples)
197 av_frame_get_buffer(
frame.get(), 0);
200 const uint8_t *
data =
buffer.constData<uint8_t>();
201 swr_convert(m_resampler.get(),
frame->extended_data,
frame->nb_samples, &
data,
207 const auto &timeBase = m_stream->time_base;
208 const auto pts = timeBase.den && timeBase.num
209 ? timeBase.den * m_samplesWritten / (m_codecContext->sample_rate * timeBase.num)
212 m_samplesWritten +=
buffer.frameCount();
220 int ret = avcodec_send_frame(m_codecContext.get(),
frame.get());
223 av_strerror(
ret, errStr, 1024);
231 return m_audioBufferQueue.size() <= 1 || m_queueDuration < m_maxQueueDuration;
233 return m_audioBufferQueue.empty();
AudioEncoder(RecordingEngine &recordingEngine, const QAudioFormat &sourceFormat, const QMediaEncoderSettings &settings)
void addBuffer(const QAudioBuffer &buffer)
void cleanup() override
Called on this thread before thread exits.
bool hasData() const override
Must return true when data is available for processing.
void processOne() override
Process one work item.
bool checkIfCanPushFrame() const override
void init() override
Called on this thread when thread starts.
void dataReady()
Wake thread from sleep and process data until hasData() returns false.
RecordingEngine & m_recordingEngine
void addPacket(AVPacketUPtr packet)
void newTimeStamp(qint64 time)
Q_WEAK_OVERLOAD void setObjectName(const QString &name)
Sets the object's name to name.
AVFrameUPtr makeAVFrame()
const AVCodec * findAVEncoder(AVCodecID codecId, const std::optional< AVHWDeviceType > &deviceType, const std::optional< PixelOrSampleFormat > &format)
AVSampleFormat adjustSampleFormat(const AVSampleFormat *supportedFormats, AVSampleFormat requested)
void setAVFrameTime(AVFrame &frame, int64_t pts, const AVRational &timeBase)
T dequeueIfPossible(std::queue< T > &queue)
AVChannelLayout adjustChannelLayout(const AVChannelLayout *supportedLayouts, const AVChannelLayout &requested)
int adjustSampleRate(const int *supportedRates, int requested)
void applyExperimentalCodecOptions(const AVCodec *codec, AVDictionary **opts)
void applyAudioEncoderOptions(const QMediaEncoderSettings &settings, const QByteArray &codecName, AVCodecContext *codec, AVDictionary **opts)
SwrContextUPtr createResampleContext(const AVAudioFormat &inputFormat, const AVAudioFormat &outputFormat)
std::unique_ptr< AVPacket, AVDeleter< decltype(&av_packet_free), &av_packet_free > > AVPacketUPtr
Combined button and popup list for selecting options.
Q_DECL_CONST_FUNCTION QT_POPCOUNT_CONSTEXPR uint qPopulationCount(quint32 v) noexcept
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLenum GLenum input
QLatin1StringView QLatin1String
QSettings settings("MySoft", "Star Runner")
[0]