Skip to content

Commit

Permalink
HLS encoder enhancements.
Browse files Browse the repository at this point in the history
- Use AAC audio instead of MP3.  This negates the need for
using --enable-libmp3lame on the command line since we now use the
built in AAC encoder in libavcodec.  This change also requires
converting the audio data to float for use by the encoder.  For
now, this commit uses C code to do the int to float conversion,
but a call to use AudioOutputUtil::toFloat() to use assembler to
do this conversion should be enabled in the near future.

- Use High 4.1 profile for encoding video with a width of >= 1024.
I also think the changes made for this support might have fixed an
issue with parts of the main profile not being used previously
for output video >= 960 in width.
  • Loading branch information
cpinkham committed Jul 16, 2012
1 parent bef289c commit f66816d
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 77 deletions.
143 changes: 74 additions & 69 deletions mythtv/libs/libmythtv/avformatwriter.cpp
Expand Up @@ -23,12 +23,14 @@
#include "mythcorecontext.h"
#include "NuppelVideoRecorder.h"
#include "avformatwriter.h"
#include "audiooutpututil.h"

extern "C" {
#if HAVE_BIGENDIAN
#include "byteswap.h"
#endif
#include "libavutil/opt.h"
#include "libavutil/samplefmt.h"
}

#define LOC QString("AVFW(%1): ").arg(m_filename)
Expand All @@ -44,16 +46,12 @@ AVFormatWriter::AVFormatWriter()
m_videoStream(NULL), m_avVideoCodec(NULL),
m_audioStream(NULL), m_avAudioCodec(NULL),
m_picture(NULL), m_tmpPicture(NULL),
m_videoOutBuf(NULL), m_videoOutBufSize(0),
m_audioSamples(NULL), m_audioOutBuf(NULL),
m_audioOutBufSize(0), m_audioInputFrameSize(0)
m_videoOutBuf(NULL),
m_audioOutBuf(NULL), m_audioOutBufSize(0),
m_audioFltBuf(NULL)
{
av_register_all();
avcodec_register_all();

// bool debug = VERBOSE_LEVEL_CHECK(VB_LIBAV, LOG_ANY);
// av_log_set_level((debug) ? AV_LOG_DEBUG : AV_LOG_ERROR);
// av_log_set_callback(myth_av_log);
}

AVFormatWriter::~AVFormatWriter()
Expand Down Expand Up @@ -86,18 +84,18 @@ AVFormatWriter::~AVFormatWriter()
m_ctx = NULL;
}

if (m_audioFltBuf)
av_free(m_audioFltBuf);

if (m_audioOutBuf)
av_free(m_audioOutBuf);

if (m_videoOutBuf)
delete [] m_videoOutBuf;
av_free(m_videoOutBuf);
}

bool AVFormatWriter::Init(void)
{
if (m_videoOutBuf)
delete [] m_videoOutBuf;

if (m_width && m_height)
m_videoOutBuf = new unsigned char[m_width * m_height * 2 + 10];

AVOutputFormat *fmt = av_guess_format(m_container.toAscii().constData(),
NULL, NULL);
if (!fmt)
Expand Down Expand Up @@ -332,21 +330,41 @@ static void bswap_16_buf(short int *buf, int buf_cnt, int audio_channels)
bool AVFormatWriter::WriteAudioFrame(unsigned char *buf, int fnum, int timecode)
{
#if HAVE_BIGENDIAN
int sample_cnt = m_audioFrameSize / m_audioBytesPerSample;
bswap_16_buf((short int*) buf, sample_cnt, m_audioChannels);
bswap_16_buf((short int*) buf, m_audioFrameSize, m_audioChannels);
#endif

int got_packet = 0;
int ret = 0;

av_init_packet(m_audPkt);

if (m_audioStream->codec->sample_fmt == AV_SAMPLE_FMT_FLT)
{
#if 1
int i = 0;
float f = 1.0f / ((1<<15) - 1);
float *out = m_audioFltBuf;
short *in = (short int *)buf;

for (; i < (m_audioFrameSize * m_audioChannels); i++)
*out++ = *in++ * f;
#else
AudioOutputUtil::toFloat(FORMAT_S16, (void *)m_audioFltBuf, (void *)buf,
m_audioFrameSize * 2 * m_audioChannels);
#endif
m_audPicture->data[0] = (unsigned char *)m_audioFltBuf;
}
else
{
m_audPicture->data[0] = buf;
}

m_audPkt->data = m_audioOutBuf;
m_audPkt->size = m_audioOutBufSize;

m_audPicture->data[0] = buf;
m_audPicture->linesize[0] = m_audioFrameSize;
m_audPicture->nb_samples = m_audioFrameSize;
m_audPicture->format = AV_SAMPLE_FMT_S16;
m_audPicture->format = m_audioStream->codec->sample_fmt;

{
QMutexLocker locker(avcodeclock);
Expand Down Expand Up @@ -374,7 +392,6 @@ bool AVFormatWriter::WriteAudioFrame(unsigned char *buf, int fnum, int timecode)

m_audPkt->dts = AV_NOPTS_VALUE;
m_audPkt->flags |= AV_PKT_FLAG_KEY;
m_audPkt->data = (uint8_t*)m_audioOutBuf;
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));
Expand Down Expand Up @@ -407,6 +424,7 @@ AVStream* AVFormatWriter::AddVideoStream(void)
{
AVCodecContext *c;
AVStream *st;
AVCodec *codec;

st = avformat_new_stream(m_ctx, NULL);
if (!st)
Expand All @@ -419,8 +437,20 @@ AVStream* AVFormatWriter::AddVideoStream(void)

c = st->codec;

codec = avcodec_find_encoder(m_ctx->oformat->video_codec);
if (!codec)
{
LOG(VB_RECORD, LOG_ERR,
LOC + "AddVideoStream(): avcodec_find_encoder() failed");
return false;
}

avcodec_get_context_defaults3(c, codec);

c->codec = codec;
c->codec_id = m_ctx->oformat->video_codec;
c->codec_type = AVMEDIA_TYPE_VIDEO;

c->bit_rate = m_videoBitrate;
c->width = m_width;
c->height = m_height;
Expand Down Expand Up @@ -454,20 +484,21 @@ AVStream* AVFormatWriter::AddVideoStream(void)
(c->width <= 320) &&
(c->bit_rate <= 768000))
{
c->level = 13;
c->level = 13;
}
else if (c->width >= 1024)
{
c->level = 41;
av_opt_set(c->priv_data, "profile", "high", 0);
}
else if (c->width >= 960)
{
if (c->width >= 1024)
c->level = 41;
else
c->level = 31;

c->level = 31;
av_opt_set(c->priv_data, "profile", "main", 0);
}
else
{
c->level = 30;
c->level = 30;
}

c->coder_type = 0;
Expand Down Expand Up @@ -502,39 +533,27 @@ AVStream* AVFormatWriter::AddVideoStream(void)
if(m_ctx->oformat->flags & AVFMT_GLOBALHEADER)
c->flags |= CODEC_FLAG_GLOBAL_HEADER;

// LOG(VB_RECORD, LOG_ERR, LOC + QString("AddVideoStream(): br: %1, gs: %2, tb: %3/%4, w: %5, h: %6").arg(c->bit_rate).arg(c->gop_size).arg(c->time_base.den).arg(c->time_base.num).arg(c->width).arg(c->height));

return st;
}

bool AVFormatWriter::OpenVideo(void)
{
AVCodec *codec;
AVCodecContext *c;

c = m_videoStream->codec;

codec = avcodec_find_encoder(c->codec_id);
if (!codec)
{
LOG(VB_RECORD, LOG_ERR,
LOC + "OpenVideo(): avcodec_find_encoder() failed");
if (m_width && m_height)
m_videoOutBuf = (unsigned char *)av_malloc(m_width * m_height * 2 + 10);
else
return false;
}

if (avcodec_open2(c, codec, NULL) < 0)
if (avcodec_open2(c, NULL, NULL) < 0)
{
LOG(VB_RECORD, LOG_ERR,
LOC + "OpenVideo(): avcodec_open() failed");
return false;
}

m_videoOutBuf = NULL;
if (!(m_ctx->oformat->flags & AVFMT_RAWPICTURE)) {
m_videoOutBufSize = 200000;
m_videoOutBuf = (unsigned char *)av_malloc(m_videoOutBufSize);
}

m_picture = AllocPicture(c->pix_fmt);
if (!m_picture)
{
Expand Down Expand Up @@ -576,8 +595,13 @@ AVStream* AVFormatWriter::AddAudioStream(void)
c->codec_id = m_ctx->oformat->audio_codec;
c->codec_type = AVMEDIA_TYPE_AUDIO;

c->sample_fmt = AV_SAMPLE_FMT_S16;
m_audioBytesPerSample = m_audioChannels * 2;
if (c->codec_id == CODEC_ID_MP3)
c->sample_fmt = AV_SAMPLE_FMT_S16;
else
c->sample_fmt = AV_SAMPLE_FMT_FLT;

m_audioBytesPerSample = m_audioChannels *
av_get_bytes_per_sample(c->sample_fmt);

c->bit_rate = m_audioBitrate;
c->sample_rate = m_audioSampleRate;
Expand All @@ -593,12 +617,6 @@ AVStream* AVFormatWriter::AddAudioStream(void)
st->time_base.num = 1;
}

// LOG(VB_RECORD, LOG_ERR, LOC + QString("AddAudioStream(): br: %1, sr, %2, c: %3, tb: %4/%5").arg(c->bit_rate).arg(c->sample_rate).arg(c->channels).arg(c->time_base.den).arg(c->time_base.num));

// Disable this for support on some devices
//if (c->codec_id == CODEC_ID_AAC)
// c->profile = FF_PROFILE_AAC_MAIN;

if(m_ctx->oformat->flags & AVFMT_GLOBALHEADER)
c->flags |= CODEC_FLAG_GLOBAL_HEADER;

Expand Down Expand Up @@ -631,28 +649,15 @@ bool AVFormatWriter::OpenAudio(void)

m_audioFrameSize = c->frame_size;

m_audioOutBufSize = (int)(1.25 * 16384 * 7200);
//m_audioOutBufSize = c->frame_size * 2 * c->channels;

m_audioOutBufSize = c->frame_size * m_audioBytesPerSample * c->channels * 2;
m_audioOutBuf = (unsigned char *)av_malloc(m_audioOutBufSize);

if (c->frame_size <= 1) {
m_audioInputFrameSize = m_audioOutBufSize / c->channels;
switch(m_audioStream->codec->codec_id) {
case CODEC_ID_PCM_S16LE:
case CODEC_ID_PCM_S16BE:
case CODEC_ID_PCM_U16LE:
case CODEC_ID_PCM_U16BE:
m_audioInputFrameSize >>= 1;
break;
default:
break;
}
} else {
m_audioInputFrameSize = c->frame_size;
if (c->sample_fmt == AV_SAMPLE_FMT_FLT)
{
int floatBufSize = m_audioFrameSize * m_audioChannels *
av_get_bytes_per_sample(c->sample_fmt);
m_audioFltBuf = (float *)av_malloc(floatBufSize);
}
m_audioSamples =
(unsigned int *)av_malloc(m_audioInputFrameSize * 2 * c->channels);

m_audPicture = avcodec_alloc_frame();
if (!m_audPicture)
Expand Down
4 changes: 1 addition & 3 deletions mythtv/libs/libmythtv/avformatwriter.h
Expand Up @@ -52,11 +52,9 @@ class MTV_PUBLIC AVFormatWriter : public FileWriterBase
AVFrame *m_audPicture;
AVPacket *m_audPkt;
unsigned char *m_videoOutBuf;
int m_videoOutBufSize;
unsigned int *m_audioSamples;
unsigned char *m_audioOutBuf;
int m_audioOutBufSize;
int m_audioInputFrameSize;
float *m_audioFltBuf;
};

#endif
Expand Down
8 changes: 3 additions & 5 deletions mythtv/programs/mythtranscode/transcode.cpp
Expand Up @@ -690,7 +690,7 @@ Transcode::Transcode(ProgramInfo *pginfo) :
hlsMode(false), hlsStreamID(-1),
hlsDisableAudioOnly(false),
hlsMaxSegments(0),
cmdContainer("mpegts"), cmdAudioCodec("libmp3lame"),
cmdContainer("mpegts"), cmdAudioCodec("aac"),
cmdVideoCodec("libx264"),
cmdWidth(480), cmdHeight(0),
cmdBitrate(800000), cmdAudioBitrate(64000)
Expand Down Expand Up @@ -1062,8 +1062,7 @@ int Transcode::TranscodeFile(const QString &inputname,
}

avfw2->SetContainer("mpegts");
avfw2->SetAudioCodec("libmp3lame");
//avfw2->SetAudioCodec("libfaac"); // --enable-libfaac to use this
avfw2->SetAudioCodec("aac");
avfw2->SetAudioBitrate(audioOnlyBitrate);
avfw2->SetAudioChannels(arb->m_channels);
avfw2->SetAudioBits(16);
Expand All @@ -1073,8 +1072,7 @@ int Transcode::TranscodeFile(const QString &inputname,

avfw->SetContainer("mpegts");
avfw->SetVideoCodec("libx264");
avfw->SetAudioCodec("libmp3lame");
//avfw->SetAudioCodec("libfaac"); // --enable-libfaac to use this
avfw->SetAudioCodec("aac");

if (hlsStreamID == -1)
hls = new HTTPLiveStream(inputname, newWidth, newHeight,
Expand Down

0 comments on commit f66816d

Please sign in to comment.