33 #include <libavcodec/avcodec.h> 36 #define FFMPEG_BUFFER_SIZE 64*1024 38 static void TorcAVLog(
void *Ptr,
int Level,
const char *Fmt, va_list VAL)
43 static QString line(QStringLiteral(
""));
44 static const int length = 255;
46 uint64_t verboseMask = VB_GENERAL;
47 int verboseLevel = LOG_DEBUG;
52 verboseLevel = LOG_EMERG;
55 verboseLevel = LOG_CRIT;
58 verboseLevel = LOG_ERR;
62 verboseLevel = LOG_DEBUG;
65 verboseLevel = LOG_INFO;
68 verboseLevel = LOG_WARNING;
78 if (line.isEmpty() && Ptr) {
79 AVClass* avc = *(AVClass**)Ptr;
80 line.sprintf(
"[%s @ %p] ", avc->item_name(Ptr), avc);
84 int bytes = vsnprintf(str, length+1, Fmt, VAL);
89 LOG(VB_GENERAL, LOG_WARNING, QStringLiteral(
"Libav log output truncated %1 of %2 bytes written").arg(length).arg(bytes));
90 str[length - 1] =
'\n';
94 if (line.endsWith(QStringLiteral(
"\n")))
96 LOG(verboseMask, verboseLevel, line.trimmed());
103 : m_formatCtx(nullptr),
107 m_ringBuffer(Buffer),
108 m_ioContext(nullptr),
109 m_audioContext(nullptr),
111 m_audioFrame(nullptr),
112 m_audioPacket(nullptr),
113 m_lastVideoPts(AV_NOPTS_VALUE),
114 m_lastAudioPts(AV_NOPTS_VALUE)
122 : m_formatCtx(nullptr),
126 m_ringBuffer(nullptr),
127 m_ioContext(nullptr),
128 m_audioContext(nullptr),
130 m_audioFrame(nullptr),
131 m_audioPacket(nullptr),
132 m_lastVideoPts(AV_NOPTS_VALUE),
133 m_lastAudioPts(AV_NOPTS_VALUE)
144 if (m_ioContext->buffer)
146 av_free(m_ioContext->buffer);
147 m_ioContext->buffer =
nullptr;
149 av_free(m_ioContext);
150 m_ioContext =
nullptr;
155 avformat_free_context(m_formatCtx);
156 m_formatCtx =
nullptr;
161 av_frame_free(&m_audioFrame);
162 m_audioFrame =
nullptr;
167 av_packet_free(&m_audioPacket);
168 m_audioPacket =
nullptr;
173 avcodec_free_context(&m_audioContext);
174 m_audioContext =
nullptr;
178 void TorcMuxer::SetupContext(
void)
180 #if (LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58,9,100)) 184 AVOutputFormat *format = av_guess_format(
"mp4",
nullptr,
nullptr);
187 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Failed to find MPEGTS muxer"));
192 m_formatCtx = avformat_alloc_context();
195 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Failed to create AVFormatContext"));
198 m_formatCtx->oformat = format;
201 void TorcMuxer::SetupIO(
void)
206 if (!m_outputFile.isEmpty())
209 if (avio_open(&m_formatCtx->pb, m_outputFile.toLocal8Bit().constData(), AVIO_FLAG_WRITE) < 0)
211 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Failed to open '%1' for output").arg(m_outputFile));
222 m_formatCtx->pb = m_ioContext;
223 m_formatCtx->flags = AVFMT_FLAG_CUSTOM_IO;
245 int size = Packet.size();
248 LOG(VB_GENERAL, LOG_WARNING, QStringLiteral(
"Cannot retrieve AVC1 codec data from packet - too short"));
254 for (; index < 2; index++)
256 if (Packet[index] == 0 && Packet[index + 1] == 0 && Packet[index + 2] == 1)
265 LOG(VB_GENERAL, LOG_WARNING, QStringLiteral(
"Failed to find start code"));
270 if ((Packet[index] & 0x1f) == 7)
271 return QStringLiteral(
"avc1.%1%2%3").arg(Packet[index + 1], 2, 16, QChar(
'0')).arg(Packet[index + 2], 2, 16, QChar(
'0')).arg(Packet[index + 3], 2, 16, QChar(
'0'));
273 LOG(VB_GENERAL, LOG_WARNING, QStringLiteral(
"Failed to find SPS"));
279 if (!Buffer || Size < 1)
283 return m_ringBuffer->
Write(Buffer, Size);
290 return m_formatCtx && m_formatCtx->nb_streams > 0 && m_created;
295 if (m_formatCtx ==
nullptr)
298 AVStream *audiostream = avformat_new_stream(m_formatCtx,
nullptr);
302 m_audioStream = m_formatCtx->nb_streams - 1;
303 audiostream->id = m_formatCtx->nb_streams - 1;
304 audiostream->codecpar->codec_id = AV_CODEC_ID_AAC;
305 audiostream->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
306 audiostream->codecpar->codec_tag = 0;
307 audiostream->codecpar->bit_rate = 64000;
308 audiostream->codecpar->sample_rate = 44100;
309 audiostream->codecpar->channels = 2;
310 audiostream->codecpar->channel_layout = AV_CH_LAYOUT_STEREO;
312 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"Audio stream id %1").arg(audiostream->id));
313 AVCodec* codec = avcodec_find_encoder(audiostream->codecpar->codec_id);
316 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Failed to find dummy audio codec"));
319 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"Found audio codec '%1'").arg(codec->name));
321 m_audioContext = avcodec_alloc_context3(codec);
324 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Failed to create audio context"));
327 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"Created audio context"));
329 avcodec_parameters_to_context(m_audioContext, audiostream->codecpar);
330 m_audioContext->sample_fmt = AV_SAMPLE_FMT_FLTP;
331 if (m_formatCtx->oformat->flags & AVFMT_GLOBALHEADER)
332 m_audioContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
333 if (avcodec_open2(m_audioContext, codec,
nullptr) < 0)
335 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Failed to open codec '%1'").arg(codec->name));
339 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"Dummy audio: frame size %1 sample_fmt %2 sample_rate %3 bitrate %4 channels %5")
340 .arg(m_audioContext->frame_size).arg(m_audioContext->sample_fmt).arg(m_audioContext->sample_rate)
341 .arg(m_audioContext->bit_rate).arg(m_audioContext->channels));
343 m_audioPacket = av_packet_alloc();
344 m_audioFrame = av_frame_alloc();
345 m_audioFrame->nb_samples = m_audioContext->frame_size;
346 m_audioFrame->format = m_audioContext->sample_fmt;
347 m_audioFrame->channel_layout = m_audioContext->channel_layout;
348 CopyExtraData(m_audioContext->extradata_size, m_audioContext->extradata, m_audioStream);
349 if (av_frame_get_buffer(m_audioFrame, 0) < 0)
351 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Failed to create audio frame buffers"));
354 return audiostream->id;
357 void TorcMuxer::WriteDummyAudio(
void)
359 if (!m_audioContext || !m_audioFrame || !m_audioPacket || m_lastVideoPts == AV_NOPTS_VALUE || !m_formatCtx)
364 if (avcodec_send_frame(m_audioContext, m_audioFrame) < 0)
366 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Error sending frame to audio encoder"));
371 int rec = avcodec_receive_packet(m_audioContext, m_audioPacket);
372 if (rec == AVERROR(EAGAIN))
376 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Error receiving packet from audio encoder"));
379 while ((m_lastVideoPts > m_lastAudioPts) || m_lastAudioPts == AV_NOPTS_VALUE)
381 int64_t duration = ((float)m_audioContext->frame_size / 44100.0) * 90000;
382 m_lastAudioPts = m_lastAudioPts == AV_NOPTS_VALUE ? m_lastVideoPts : m_lastAudioPts + duration;
383 m_audioPacket->pts = m_lastAudioPts;
384 m_audioPacket->dts = m_lastAudioPts;
385 m_audioPacket->stream_index = m_audioStream;
386 if (av_write_frame(m_formatCtx, m_audioPacket) < 0)
387 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Failed to write audio frame to muxer"));
389 av_packet_unref(m_audioPacket);
400 AVStream *h264video = avformat_new_stream(m_formatCtx,
nullptr);
404 h264video->id = m_formatCtx->nb_streams - 1;
405 h264video->codecpar->codec_id = AV_CODEC_ID_H264;
406 h264video->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
407 h264video->codecpar->codec_tag = 0;
408 h264video->codecpar->bit_rate = Bitrate;
409 h264video->codecpar->profile = Profile;
410 h264video->codecpar->level = 30;
411 h264video->codecpar->format = AV_PIX_FMT_YUV420P;
412 h264video->codecpar->width = Width;
413 h264video->codecpar->height = Height;
414 return h264video->id;
417 void TorcMuxer::CopyExtraData(
int Size,
void* Source,
int Stream)
419 if (!m_formatCtx || !Source || Size < 1)
422 if (m_formatCtx->nb_streams <= 0 || (uint)Stream >= m_formatCtx->nb_streams || Stream < 0)
424 LOG(VB_GENERAL, LOG_INFO, QStringLiteral(
"Cannot copy extradata - invalid stream"));
428 AVStream *stream = m_formatCtx->streams[Stream];
429 stream->codecpar->extradata = (uint8_t*)av_mallocz(Size + AV_INPUT_BUFFER_PADDING_SIZE);
430 stream->codecpar->extradata_size = Size;
431 memcpy(stream->codecpar->extradata, Source, Size);
436 if (!m_formatCtx || !m_created || !Packet)
441 CopyExtraData(Packet->size, Packet->data, Packet->stream_index);
453 LOG(VB_GENERAL, LOG_WARNING, QStringLiteral(
"Ignoring packet - stream not started (waiting for config?)"));
457 int result = av_write_frame(m_formatCtx, Packet);
459 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Failed to write video frame"));
461 m_lastVideoPts = Packet->pts;
471 av_write_frame(m_formatCtx,
nullptr);
477 void TorcMuxer::Start(
void)
481 av_dump_format(m_formatCtx, 0,
"stdout", 1);
482 AVDictionary *opts =
nullptr;
483 av_dict_set(&opts,
"movflags",
"frag_custom+dash+delay_moov", 0);
484 if (avformat_write_header(m_formatCtx, &opts))
485 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Failed to set demuxer options"));
492 if (m_audioContext && m_audioPacket)
495 avcodec_send_frame(m_audioContext,
nullptr);
496 while (avcodec_receive_packet(m_audioContext, m_audioPacket) >= 0)
497 av_packet_unref(m_audioPacket);
502 if (av_write_trailer(m_formatCtx))
503 LOG(VB_GENERAL, LOG_ERR, QStringLiteral(
"Failed to write stream trailer"));
int AddH264Stream(int Width, int Height, int Profile, int Bitrate)
#define FFMPEG_BUFFER_SIZE
int AddDummyAudioStream(void)
static QString GetAVCCodec(const QByteArray &Packet)
Determine the 3 byte H.264 codec descriptor string.
bool AddPacket(AVPacket *Packet, bool CodecConfig)
void FinishSegment(bool Init)
int FinishSegment(bool Init)
Finish the current segment and start a new one.
int WriteAVPacket(uint8_t *Buffer, int Size)
static int AVWritePacket(void *Opaque, uint8_t *Buffer, int Size)
#define VERBOSE_LEVEL_NONE
int Write(QByteArray *Data, int Size)
#define LOG(_MASK_, _LEVEL_, _STRING_)
static void TorcAVLog(void *Ptr, int Level, const char *Fmt, va_list VAL)
TorcMuxer(const QString &File)
#define VERBOSE_LEVEL_CHECK(_MASK_, _LEVEL_)