Permalink
Browse files

Various HLS bugfixes.

- Use av_malloc() to allocate the temporary newFrame frame buffer.
- Fix drifting timecode calculations in AudioReencodeBuffer::AddData().
- Fix race condition in audio encoding loop in Transcode::TranscodeFile()
- Make durations of segments more consistent (till need to fix duration
  in EXTINF tags).
- Fix issue with incorrect timecodes in output due to frames buffered in
  video encoder.
- Re-instate MP3 audio usage over AAC.  The AAC encoder seems to be
  working fine in all players I've tested except for iOS, so reverting
  back to the MP3 encoder for now.

NOTE1: This will require you to run configure with --enable-libmp3lame
       to enable the libmp3lame encoder in libavcodec.  If you wish to
       help debug AAC audio in HLS, you can insert a setting called
       'HLSAACAUDIO' with a value of 1 into your settings table.

NOTE2: There is a still an issue I'm seeing with video on iOS,
       past/previous video frames sometimes flash on the screen.  This
       does not happen in mythavtest, mplayer, or JW Player and is
       still being investigated.
  • Loading branch information...
1 parent 4a40145 commit 628ea66a84d386e414d6beafa4cba5ae227dcd1b @cpinkham cpinkham committed Jul 19, 2012
@@ -12,7 +12,7 @@
/// Update this whenever the plug-in API changes.
/// Including changes in the libmythbase, libmyth, libmythtv, libmythav* and
/// libmythui class methods used by plug-ins.
-#define MYTH_BINARY_VERSION "0.26.20120620-1"
+#define MYTH_BINARY_VERSION "0.26.20120719-1"
/** \brief Increment this whenever the MythTV network protocol changes.
*
@@ -237,13 +237,14 @@ bool AVFormatWriter::CloseFile(void)
bool AVFormatWriter::NextFrameIsKeyFrame(void)
{
- if ((m_framesWritten % m_keyFrameDist) == 0)
+ if ((m_bufferedVideoFrameTypes.isEmpty()) ||
+ (m_bufferedVideoFrameTypes.first() == AV_PICTURE_TYPE_I))
return true;
return false;
}
-bool AVFormatWriter::WriteVideoFrame(VideoFrame *frame)
+int AVFormatWriter::WriteVideoFrame(VideoFrame *frame)
{
AVCodecContext *c;
@@ -275,6 +276,9 @@ bool AVFormatWriter::WriteVideoFrame(VideoFrame *frame)
int got_pkt = 0;
int ret = 0;
+ m_bufferedVideoFrameTimes.push_back(frame->timecode);
+ m_bufferedVideoFrameTypes.push_back(m_picture->pict_type);
+
av_init_packet(m_pkt);
m_pkt->data = (unsigned char *)m_videoOutBuf;
m_pkt->size = len;
@@ -285,18 +289,28 @@ bool AVFormatWriter::WriteVideoFrame(VideoFrame *frame)
m_picture, &got_pkt);
}
- if (ret < 0 || !got_pkt)
+ if (ret < 0)
{
-#if 0
- LOG(VB_RECORD, LOG_ERR, QString("WriteVideoFrame(): cs: %1, mfw: %2, tc: %3, fn: %4").arg(m_pkt->size).arg(m_framesWritten).arg(frame->timecode).arg(frame->frameNumber));
-#endif
- return false;
+ LOG(VB_RECORD, LOG_ERR, "avcodec_encode_video2() failed");
+ return ret;
+ }
+
+ if (!got_pkt)
+ {
+ //LOG(VB_RECORD, LOG_DEBUG, QString("WriteVideoFrame(): Frame Buffered: cs: %1, mfw: %2, f->tc: %3, fn: %4, pt: %5").arg(m_pkt->size).arg(m_framesWritten).arg(frame->timecode).arg(frame->frameNumber).arg(m_picture->pict_type));
+ return ret;
}
if ((m_framesWritten % m_keyFrameDist) == 0)
m_pkt->flags |= AV_PKT_FLAG_KEY;
long long tc = frame->timecode;
+
+ if (!m_bufferedVideoFrameTimes.isEmpty())
+ tc = m_bufferedVideoFrameTimes.takeFirst();
+ if (!m_bufferedVideoFrameTypes.isEmpty())
+ m_bufferedVideoFrameTypes.pop_front();
+
if (m_startingTimecodeOffset == -1)
m_startingTimecodeOffset = tc;
tc -= m_startingTimecodeOffset;
@@ -305,15 +319,16 @@ bool AVFormatWriter::WriteVideoFrame(VideoFrame *frame)
m_pkt->dts = AV_NOPTS_VALUE;
m_pkt->stream_index= m_videoStream->index;
- // LOG(VB_RECORD, LOG_ERR, QString("WriteVideoFrame(): cs: %1, mfw: %2, pkt->pts: %3, tc: %4, fn: %5, pic->pts: %6").arg(m_pkt->size).arg(m_framesWritten).arg(m_pkt->pts).arg(frame->timecode).arg(frame->frameNumber).arg(m_picture->pts));
+ //LOG(VB_RECORD, LOG_DEBUG, QString("WriteVideoFrame(): cs: %1, mfw: %2, pkt->pts: %3, tc: %4, fn: %5, pic->pts: %6, f->tc: %7, pt: %8").arg(m_pkt->size).arg(m_framesWritten).arg(m_pkt->pts).arg(tc).arg(frame->frameNumber).arg(m_picture->pts).arg(frame->timecode).arg(m_picture->pict_type));
ret = av_interleaved_write_frame(m_ctx, m_pkt);
if (ret != 0)
LOG(VB_RECORD, LOG_ERR, LOC + "WriteVideoFrame(): "
"av_interleaved_write_frame couldn't write Video");
+ frame->timecode = tc + m_startingTimecodeOffset;
m_framesWritten++;
- return true;
+ return 1;
}
#if HAVE_BIGENDIAN
@@ -327,7 +342,7 @@ static void bswap_16_buf(short int *buf, int buf_cnt, int audio_channels)
}
#endif
-bool AVFormatWriter::WriteAudioFrame(unsigned char *buf, int fnum, int timecode)
+int AVFormatWriter::WriteAudioFrame(unsigned char *buf, int fnum, long long &timecode)
{
#if HAVE_BIGENDIAN
bswap_16_buf((short int*) buf, m_audioFrameSize, m_audioChannels);
@@ -356,21 +371,31 @@ bool AVFormatWriter::WriteAudioFrame(unsigned char *buf, int fnum, int timecode)
m_audPicture->nb_samples = m_audioFrameSize;
m_audPicture->format = m_audioStream->codec->sample_fmt;
+ m_bufferedAudioFrameTimes.push_back(timecode);
+
{
QMutexLocker locker(avcodeclock);
ret = avcodec_encode_audio2(m_audioStream->codec, m_audPkt,
m_audPicture, &got_packet);
}
- if (ret < 0 || !got_packet)
+ if (ret < 0)
{
-#if 0
- LOG(VB_RECORD, LOG_ERR, QString("WriteAudioFrame(): No Encoded Data: cs: %1, mfw: %2, tc: %3, fn: %4").arg(m_audPkt->size).arg(m_framesWritten).arg(timecode).arg(fnum));
-#endif
- return false;
+ LOG(VB_RECORD, LOG_ERR, "avcodec_encode_audio2() failed");
+ return ret;
+ }
+
+ if (!got_packet)
+ {
+ //LOG(VB_RECORD, LOG_ERR, QString("WriteAudioFrame(): Frame Buffered: cs: %1, mfw: %2, f->tc: %3, fn: %4").arg(m_audPkt->size).arg(m_framesWritten).arg(timecode).arg(fnum));
+ return ret;
}
long long tc = timecode;
+
+ if (m_bufferedAudioFrameTimes.size())
+ tc = m_bufferedAudioFrameTimes.takeFirst();
+
if (m_startingTimecodeOffset == -1)
m_startingTimecodeOffset = tc;
tc -= m_startingTimecodeOffset;
@@ -384,20 +409,21 @@ bool AVFormatWriter::WriteAudioFrame(unsigned char *buf, int fnum, int timecode)
m_audPkt->flags |= AV_PKT_FLAG_KEY;
m_audPkt->stream_index = m_audioStream->index;
- // LOG(VB_RECORD, LOG_ERR, QString("WriteAudioFrame(): cs: %1, mfw: %2, pkt->pts: %3, tc: %4, fn: %5").arg(m_audPkt->size).arg(m_framesWritten).arg(m_audPkt->pts).arg(timecode).arg(fnum));
+ //LOG(VB_RECORD, LOG_ERR, QString("WriteAudioFrame(): cs: %1, mfw: %2, pkt->pts: %3, tc: %4, fn: %5, f->tc: %6").arg(m_audPkt->size).arg(m_framesWritten).arg(m_audPkt->pts).arg(tc).arg(fnum).arg(timecode));
ret = av_interleaved_write_frame(m_ctx, m_audPkt);
if (ret != 0)
LOG(VB_RECORD, LOG_ERR, LOC + "WriteAudioFrame(): "
"av_interleaved_write_frame couldn't write Audio");
+ timecode = tc + m_startingTimecodeOffset;
- return true;
+ return 1;
}
-bool AVFormatWriter::WriteTextFrame(int vbimode, unsigned char *buf, int len,
- int timecode, int pagenr)
+int AVFormatWriter::WriteTextFrame(int vbimode, unsigned char *buf, int len,
+ long long timecode, int pagenr)
{
- return true;
+ return 1;
}
bool AVFormatWriter::ReOpen(QString filename)
@@ -458,6 +484,7 @@ AVStream* AVFormatWriter::AddVideoStream(void)
c->gop_size = m_keyFrameDist;
c->pix_fmt = PIX_FMT_YUV420P;
c->thread_count = m_encodingThreadCount;
+ c->thread_type = FF_THREAD_SLICE;
if (c->codec_id == CODEC_ID_MPEG2VIDEO) {
c->max_b_frames = 2;
@@ -4,6 +4,8 @@
#include "filewriterbase.h"
#include "avfringbuffer.h"
+#include <QList>
+
#undef HAVE_AV_CONFIG_H
extern "C" {
#include "libavcodec/avcodec.h"
@@ -20,10 +22,10 @@ class MTV_PUBLIC AVFormatWriter : public FileWriterBase
bool OpenFile(void);
bool CloseFile(void);
- bool WriteVideoFrame(VideoFrame *frame);
- bool WriteAudioFrame(unsigned char *buf, int fnum, int timecode);
- bool WriteTextFrame(int vbimode, unsigned char *buf, int len,
- int timecode, int pagenr);
+ int WriteVideoFrame(VideoFrame *frame);
+ int WriteAudioFrame(unsigned char *buf, int fnum, long long &timecode);
+ int WriteTextFrame(int vbimode, unsigned char *buf, int len,
+ long long timecode, int pagenr);
bool NextFrameIsKeyFrame(void);
bool ReOpen(QString filename);
@@ -55,6 +57,10 @@ class MTV_PUBLIC AVFormatWriter : public FileWriterBase
unsigned char *m_audioOutBuf;
int m_audioOutBufSize;
float *m_audioFltBuf;
+
+ QList<long long> m_bufferedVideoFrameTimes;
+ QList<int> m_bufferedVideoFrameTypes;
+ QList<long long> m_bufferedAudioFrameTimes;
};
#endif
@@ -40,18 +40,18 @@ FileWriterBase::~FileWriterBase()
{
}
-bool FileWriterBase::WriteVideoFrame(VideoFrame *frame)
+int FileWriterBase::WriteVideoFrame(VideoFrame *frame)
{
LOG(VB_RECORD, LOG_ERR, LOC + "WriteVideoFrame(): Shouldn't be here!");
- return false;
+ return 1;
}
-bool FileWriterBase::WriteAudioFrame(unsigned char *buf, int fnum, int timecode)
+int FileWriterBase::WriteAudioFrame(unsigned char *buf, int fnum, long long &timecode)
{
LOG(VB_RECORD, LOG_ERR, LOC + "WriteAudioFrame(): Shouldn't be here!");
- return false;
+ return 1;
}
/* vim: set expandtab tabstop=4 shiftwidth=4: */
@@ -15,11 +15,11 @@ class MTV_PUBLIC FileWriterBase
virtual bool OpenFile(void) { return true; }
virtual bool CloseFile(void) { return true; }
- virtual bool WriteVideoFrame(VideoFrame *frame);
- virtual bool WriteAudioFrame(unsigned char *buf, int fnum, int timecode);
- virtual bool WriteTextFrame(int vbimode, unsigned char *buf, int len,
- int timecode, int pagenr) { return true; }
- virtual bool WriteSeekTable(void) { return true; }
+ virtual int WriteVideoFrame(VideoFrame *frame);
+ virtual int WriteAudioFrame(unsigned char *buf, int fnum, long long &timecode);
+ virtual int WriteTextFrame(int vbimode, unsigned char *buf, int len,
+ long long timecode, int pagenr) { return 1; }
+ virtual int WriteSeekTable(void) { return 1; }
virtual bool SwitchToNextFile(void) { return false; }
Oops, something went wrong.

0 comments on commit 628ea66

Please sign in to comment.