Skip to content

Commit

Permalink
Various HLS bugfixes.
Browse files Browse the repository at this point in the history
- 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
cpinkham committed Jul 19, 2012
1 parent 4a40145 commit 628ea66
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 65 deletions.
2 changes: 1 addition & 1 deletion mythtv/libs/libmythbase/mythversion.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down
67 changes: 47 additions & 20 deletions mythtv/libs/libmythtv/avformatwriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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
Expand All @@ -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);
Expand Down Expand Up @@ -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;
Expand All @@ -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)
Expand Down Expand Up @@ -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;
Expand Down
14 changes: 10 additions & 4 deletions mythtv/libs/libmythtv/avformatwriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include "filewriterbase.h"
#include "avfringbuffer.h"

#include <QList>

#undef HAVE_AV_CONFIG_H
extern "C" {
#include "libavcodec/avcodec.h"
Expand All @@ -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);
Expand Down Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions mythtv/libs/libmythtv/filewriterbase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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: */
Expand Down
10 changes: 5 additions & 5 deletions mythtv/libs/libmythtv/filewriterbase.h
Original file line number Diff line number Diff line change
Expand Up @@ -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; }

Expand Down
Loading

0 comments on commit 628ea66

Please sign in to comment.