diff --git a/mythtv/configure b/mythtv/configure
index 32e0098b9f3..4d9c025e3b4 100755
--- a/mythtv/configure
+++ b/mythtv/configure
@@ -135,6 +135,7 @@ Advanced options (experts only):
   --disable-vaapi          disable VAAPI hardware accelerated video decoding
   --disable-openmax        disable OpenMAX hardware accelerated video decoding
   --disable-dxva2          disable hardware accelerated decoding on windows
+  --disable-mediacodec     disable hardware accelerated decoding on android
   --disable-opengl-video   disable OpenGL based video display
   --disable-opengl-themepainter disable OpenGL based theme painting
   --disable-libass         disable libass SSA/ASS subtitle support
@@ -2038,6 +2039,7 @@ USING_LIST='
     vaapi
     vdpau
     openmax
+    mediacodec
 '
 
 CMDLINE_SELECT="
@@ -2763,6 +2765,7 @@ enable libcrypto
 enable libdns_sd
 enable libxml2
 enable lirc
+enable mediacodec
 enable mheg
 enable mythtranscode
 enable opengl
@@ -6971,9 +6974,16 @@ ffmpeg_optset extra_cxxflags extra_ldflags target_os
 ffmpeg_optset pkg_config prefix libdir as objcc dep_cc host_cc
 ffmpeg_optset host_ld
 ffmpeg_optenable cross_compile libmp3lame libx264 libx265 libvpx libxvid
-ffmpeg_optenable vdpau vaapi libxml2 libass dxva2 mediacodec
-ffmpeg_optenable jni libbluray libfontconfig libfreetype libiec61883
+ffmpeg_optenable vdpau vaapi libxml2 libass dxva2
+ffmpeg_optenable libbluray libfontconfig libfreetype libiec61883
 ffmpeg_optenable crystalhd sdl2 ffplay
+if test $target_os = "android"; then
+    enabled mediacodec && enable jni
+    ffmpeg_optenable mediacodec jni
+else
+    disable mediacodec
+fi
+
 ffmpeg_extra_cflags="$extra_cflags -w"
 
 ## Call FFmpeg configure here
diff --git a/mythtv/external/FFmpeg/libavcodec/mediacodec_sw_buffer.c b/mythtv/external/FFmpeg/libavcodec/mediacodec_sw_buffer.c
index 92428e85f02..30a53f05b36 100644
--- a/mythtv/external/FFmpeg/libavcodec/mediacodec_sw_buffer.c
+++ b/mythtv/external/FFmpeg/libavcodec/mediacodec_sw_buffer.c
@@ -150,7 +150,7 @@ void ff_mediacodec_sw_buffer_copy_yuv420_semi_planar(AVCodecContext *avctx,
         } else if (i == 1) {
             height = avctx->height / 2;
 
-            src += s->slice_height * s->stride;
+            src += s->height * s->stride;
             src += s->crop_top * s->stride;
             src += s->crop_left;
         }
diff --git a/mythtv/libs/libmythtv/avformatdecoder.cpp b/mythtv/libs/libmythtv/avformatdecoder.cpp
index 2c3a1985a05..eef825d766e 100644
--- a/mythtv/libs/libmythtv/avformatdecoder.cpp
+++ b/mythtv/libs/libmythtv/avformatdecoder.cpp
@@ -62,6 +62,14 @@ extern "C" {
 #include "vaapicontext.h"
 #endif
 
+#ifdef USING_MEDIACODEC
+#include "mediacodeccontext.h"
+extern "C" {
+#include "libavcodec/jni.h"
+}
+#include <QtAndroidExtras>
+#endif
+
 extern "C" {
 #include "libavutil/avutil.h"
 #include "libavutil/error.h"
@@ -276,30 +284,33 @@ static void myth_av_log(void *ptr, int level, const char* fmt, va_list vl)
     static QString full_line("");
     static const int msg_len = 255;
     static QMutex string_lock;
-    uint64_t   verbose_mask  = VB_GENERAL;
-    LogLevel_t verbose_level = LOG_DEBUG;
+    uint64_t   verbose_mask  = VB_LIBAV;
+    LogLevel_t verbose_level = LOG_EMERG;
 
     // determine mythtv debug level from av log level
     switch (level)
     {
         case AV_LOG_PANIC:
             verbose_level = LOG_EMERG;
+            verbose_mask |= VB_GENERAL;
             break;
         case AV_LOG_FATAL:
             verbose_level = LOG_CRIT;
+            verbose_mask |= VB_GENERAL;
             break;
         case AV_LOG_ERROR:
             verbose_level = LOG_ERR;
-            verbose_mask |= VB_LIBAV;
             break;
-        case AV_LOG_DEBUG:
-        case AV_LOG_VERBOSE:
+        case AV_LOG_WARNING:
+            verbose_level = LOG_WARNING;
+            break;
         case AV_LOG_INFO:
-            verbose_level = LOG_DEBUG;
-            verbose_mask |= VB_LIBAV;
+            verbose_level = LOG_INFO;
             break;
-        case AV_LOG_WARNING:
-            verbose_mask |= VB_LIBAV;
+        case AV_LOG_VERBOSE:
+        case AV_LOG_DEBUG:
+        case AV_LOG_TRACE:
+            verbose_level = LOG_DEBUG;
             break;
         default:
             return;
@@ -374,6 +385,10 @@ void AvFormatDecoder::GetDecoders(render_opts &opts)
     opts.decoders->append("vaapi");
     (*opts.equiv_decoders)["vaapi"].append("dummy");
 #endif
+#ifdef USING_MEDIACODEC
+    opts.decoders->append("mediacodec");
+    (*opts.equiv_decoders)["mediacodec"].append("dummy");
+#endif
 
     PrivateDecoder::GetDecoders(opts);
 }
@@ -405,6 +420,8 @@ AvFormatDecoder::AvFormatDecoder(MythPlayer *parent,
       pts_detected(false),
       reordered_pts_detected(false),
       pts_selected(true),
+      use_frame_timing(false),
+      flush_discard(0),
       force_dts_timestamps(false),
       playerFlags(flags),
       video_codec_id(kCodec_NONE),
@@ -433,8 +450,6 @@ AvFormatDecoder::AvFormatDecoder(MythPlayer *parent,
     audioSamples = (uint8_t *)av_mallocz(AudioOutput::MAX_SIZE_BUFFER);
     ccd608->SetIgnoreTimecode(true);
 
-    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);
 
     audioIn.sample_size = -32; // force SetupAudioStream to run once
@@ -742,6 +757,7 @@ bool AvFormatDecoder::DoFastForward(long long desiredFrame, bool discardFrames)
 
         lastKey = (long long)((newts*(long double)fps)/AV_TIME_BASE);
         framesPlayed = lastKey;
+        fpsSkip = 0;
         framesRead = lastKey;
 
         normalframes = (exactseeks) ? desiredFrame - framesPlayed : 0;
@@ -753,6 +769,7 @@ bool AvFormatDecoder::DoFastForward(long long desiredFrame, bool discardFrames)
         LOG(VB_GENERAL, LOG_INFO, LOC + "No DTS Seeking Hack!");
         no_dts_hack = true;
         framesPlayed = desiredFrame;
+        fpsSkip = 0;
         framesRead = desiredFrame;
         normalframes = 0;
     }
@@ -817,7 +834,11 @@ void AvFormatDecoder::SeekReset(long long newKey, uint skipFrames,
             // enc->internal = NULL and cause a segfault in
             // avcodec_flush_buffers
             if (enc && enc->internal)
+            {
                 avcodec_flush_buffers(enc);
+                if (fpsMultiplier > 1)
+                    flush_discard = 4;
+            }
         }
         if (private_dec)
             private_dec->Reset();
@@ -844,6 +865,7 @@ void AvFormatDecoder::SeekReset(long long newKey, uint skipFrames,
             if (!no_dts_hack)
             {
                 framesPlayed = lastKey;
+                fpsSkip = 0;
                 framesRead = lastKey;
             }
 
@@ -1004,9 +1026,6 @@ extern "C" void HandleStreamChange(void *data)
 int AvFormatDecoder::FindStreamInfo(void)
 {
     QMutexLocker lock(avcodeclock);
-    // Suppress ffmpeg logging unless "-v libav --loglevel debug"
-    if (!VERBOSE_LEVEL_CHECK(VB_LIBAV, LOG_DEBUG))
-        silence_ffmpeg_logging = true;
     int retval = avformat_find_stream_info(ic, NULL);
     silence_ffmpeg_logging = false;
     // ffmpeg 3.0 is returning -1 code when there is a channel
@@ -1540,6 +1559,21 @@ enum AVPixelFormat get_format_vaapi(struct AVCodecContext *avctx,
 }
 #endif
 
+#ifdef USING_MEDIACODEC
+static enum AVPixelFormat get_format_mediacodec(struct AVCodecContext *avctx,
+                                           const enum AVPixelFormat *valid_fmts)
+{
+    enum AVPixelFormat ret = *valid_fmts; // default to first
+    while (*valid_fmts != AV_PIX_FMT_NONE) {
+        if (*valid_fmts == AV_PIX_FMT_YUV420P)
+            ret = AV_PIX_FMT_YUV420P;
+        valid_fmts++;
+    }
+    return ret;
+}
+#endif
+
+
 static bool IS_DR1_PIX_FMT(const enum AVPixelFormat fmt)
 {
     switch (fmt)
@@ -1575,7 +1609,7 @@ void AvFormatDecoder::InitVideoCodec(AVStream *stream, AVCodecContext *enc,
     enc->debug = 0;
     // enc->error_rate = 0;
 
-    AVCodec *codec = avcodec_find_decoder(enc->codec_id);
+    const AVCodec *codec = enc->codec;
 
     if (selectedStream)
     {
@@ -1612,6 +1646,14 @@ void AvFormatDecoder::InitVideoCodec(AVStream *stream, AVCodecContext *enc,
         enc->slice_flags     = SLICE_FLAG_CODED_ORDER | SLICE_FLAG_ALLOW_FIELD;
     }
     else
+#endif
+#ifdef USING_MEDIACODEC
+    if (CODEC_IS_MEDIACODEC(codec))
+    {
+        enc->get_format      = get_format_mediacodec;
+        enc->slice_flags     = SLICE_FLAG_CODED_ORDER | SLICE_FLAG_ALLOW_FIELD;
+    }
+    else
 #endif
     if (codec && codec->capabilities & AV_CODEC_CAP_DR1)
     {
@@ -1679,7 +1721,7 @@ void AvFormatDecoder::InitVideoCodec(AVStream *stream, AVCodecContext *enc,
         }
 
         m_parent->SetKeyframeDistance(keyframedist);
-        AVCodec *codec = avcodec_find_decoder(enc->codec_id);
+        const AVCodec *codec = enc->codec;
         QString codecName;
         if (codec)
             codecName = codec->name;
@@ -2368,7 +2410,7 @@ int AvFormatDecoder::ScanStreams(bool novideo)
 
             if (averror_count > SEQ_PKT_ERR_MAX)
                 gCodecMap->freeCodecContext(ic->streams[selTrack]);
-            AVCodecContext *enc = gCodecMap->getCodecContext(ic->streams[selTrack]);
+            AVCodecContext *enc = gCodecMap->getCodecContext(ic->streams[selTrack], codec);
             StreamInfo si(selTrack, 0, 0, 0, 0);
 
             tracks[kTrackTypeVideo].push_back(si);
@@ -2393,10 +2435,9 @@ int AvFormatDecoder::ScanStreams(bool novideo)
             uint height = max(dim.height(), 16);
             QString dec = "ffmpeg";
             uint thread_count = 1;
-            AVCodec *codec1 = avcodec_find_decoder(enc->codec_id);
             QString codecName;
-            if (codec1)
-                codecName = codec1->name;
+            if (enc->codec)
+                codecName = enc->codec->name;
             if (enc->framerate.den && enc->framerate.num)
                 fps = float(enc->framerate.num) / float(enc->framerate.den);
             else
@@ -2473,7 +2514,7 @@ int AvFormatDecoder::ScanStreams(bool novideo)
                 }
 #endif // USING_GLVAAPI
 #ifdef USING_DXVA2
-                if (!foundgpudecode)
+                if (!foundgpudecoder)
                 {
                     MythCodecID dxva2_mcid;
                     AVPixelFormat pix_fmt = AV_PIX_FMT_YUV420P;
@@ -2489,12 +2530,25 @@ int AvFormatDecoder::ScanStreams(bool novideo)
                     }
                 }
 #endif // USING_DXVA2
-                if (foundgpudecoder)
+#ifdef USING_MEDIACODEC
+                if (!foundgpudecoder)
                 {
-                    enc->codec_id = (AVCodecID) myth2av_codecid(video_codec_id);
+                    MythCodecID mediacodec_mcid;
+                    AVPixelFormat pix_fmt = AV_PIX_FMT_YUV420P;
+                    mediacodec_mcid = MediaCodecContext::GetBestSupportedCodec(
+                        &codec, dec, mpeg_version(enc->codec_id),
+                        pix_fmt);
+
+                    if (codec_is_mediacodec(mediacodec_mcid))
+                    {
+                        gCodecMap->freeCodecContext(ic->streams[selTrack]);
+                        enc = gCodecMap->getCodecContext(ic->streams[selTrack], codec);
+                        video_codec_id = mediacodec_mcid;
+                        foundgpudecoder = true;
+                    }
                 }
+#endif // USING_MEDIACODEC
             }
-
             // default to mpeg2
             if (video_codec_id == kCodec_NONE)
             {
@@ -2502,10 +2556,6 @@ int AvFormatDecoder::ScanStreams(bool novideo)
                     "Unknown video codec - defaulting to MPEG2");
                 video_codec_id = kCodec_MPEG2;
             }
-            else
-            {
-                codec = avcodec_find_decoder(enc->codec_id);
-            }
 
             // Use a PrivateDecoder if allowed in playerFlags AND matched
             // via the decoder name
@@ -2516,6 +2566,11 @@ int AvFormatDecoder::ScanStreams(bool novideo)
             if (!codec_is_std(video_codec_id))
                 thread_count = 1;
 
+            use_frame_timing = false;
+            if (! private_dec
+                && (codec_is_std(video_codec_id) || codec_is_mediacodec(video_codec_id)))
+                use_frame_timing = true;
+
             if (FlagIsSet(kDecodeSingleThreaded))
                 thread_count = 1;
 
@@ -2531,7 +2586,7 @@ int AvFormatDecoder::ScanStreams(bool novideo)
             ScanATSCCaptionStreams(selTrack);
             UpdateATSCCaptionTracks();
 
-            LOG(VB_PLAYBACK, LOG_INFO, LOC +
+            LOG(VB_GENERAL, LOG_INFO, LOC +
                 QString("Using %1 for video decoding")
                 .arg(GetCodecDecoderName()));
 
@@ -2611,6 +2666,10 @@ bool AvFormatDecoder::OpenAVCodec(AVCodecContext *avctx, const AVCodec *codec)
 {
     QMutexLocker locker(avcodeclock);
 
+#ifdef USING_MEDIACODEC
+    if (QString("mediacodec") == codec->wrapper_name)
+        av_jni_set_java_vm(QAndroidJniEnvironment::javaVM(), NULL);
+#endif
     int ret = avcodec_open2(avctx, codec, NULL);
     if (ret < 0)
     {
@@ -3536,8 +3595,12 @@ bool AvFormatDecoder::PreProcessVideoPacket(AVStream *curstream, AVPacket *pkt)
     return true;
 }
 
+// Maximum retries - 500 = 5 seconds
+#define PACKET_MAX_RETRIES 5000
+#define RETRY_WAIT_TIME 10000   // microseconds
 bool AvFormatDecoder::ProcessVideoPacket(AVStream *curstream, AVPacket *pkt)
 {
+    int retryCount = 0;
     int ret = 0, gotpicture = 0;
     int64_t pts = 0;
     AVCodecContext *context = gCodecMap->getCodecContext(curstream);
@@ -3551,133 +3614,199 @@ bool AvFormatDecoder::ProcessVideoPacket(AVStream *curstream, AVPacket *pkt)
     if (pkt->pts != (int64_t)AV_NOPTS_VALUE)
         pts_detected = true;
 
-    avcodeclock->lock();
-    if (private_dec)
+    bool tryAgain = true;
+    bool sentPacket = false;
+    int ret2 = 0;
+    while (tryAgain)
     {
-        if (QString(ic->iformat->name).contains("avi") || !pts_detected)
-            pkt->pts = pkt->dts;
-        // TODO disallow private decoders for dvd playback
-        // N.B. we do not reparse the frame as it breaks playback for
-        // everything but libmpeg2
-        ret = private_dec->GetFrame(curstream, mpa_pic, &gotpicture, pkt);
-    }
-    else
-    {
-        context->reordered_opaque = pkt->pts;
-        //  SUGGESTION
-        //  Now that avcodec_decode_video2 is deprecated and replaced
-        //  by 2 calls (receive frame and send packet), this could be optimized
-        //  into separate routines or separate threads.
-        //  Also now that it always consumes a whole buffer some code
-        //  in the caller may be able to be optimized.
-        ret = avcodec_receive_frame(context, mpa_pic);
-        if (ret == 0)
-            gotpicture = 1;
-        if (ret == AVERROR(EAGAIN))
+        tryAgain = false;
+        gotpicture = 0;
+        avcodeclock->lock();
+        if (private_dec)
+        {
+            if (QString(ic->iformat->name).contains("avi") || !pts_detected)
+                pkt->pts = pkt->dts;
+            // TODO disallow private decoders for dvd playback
+            // N.B. we do not reparse the frame as it breaks playback for
+            // everything but libmpeg2
+            ret = private_dec->GetFrame(curstream, mpa_pic, &gotpicture, pkt);
+            sentPacket = true;
+        }
+        else
+        {
+            if (!use_frame_timing)
+                context->reordered_opaque = pkt->pts;
+
+            //  SUGGESTION
+            //  Now that avcodec_decode_video2 is deprecated and replaced
+            //  by 2 calls (receive frame and send packet), this could be optimized
+            //  into separate routines or separate threads.
+            //  Also now that it always consumes a whole buffer some code
+            //  in the caller may be able to be optimized.
             ret = 0;
-        if (ret == 0)
-            ret = avcodec_send_packet(context, pkt);
-        // The code assumes that there is always space to add a new
-        // packet. This seems risky but has always worked.
-        // It should actually check if (ret == AVERROR(EAGAIN)) and then keep
-        // the packet around and try it again after processing the frame
-        // received here.
-    }
-    avcodeclock->unlock();
+            ret = avcodec_receive_frame(context, mpa_pic);
 
-    if (ret < 0)
-    {
-        char error[AV_ERROR_MAX_STRING_SIZE];
-        LOG(VB_GENERAL, LOG_ERR, LOC +
-            QString("video decode error: %1 (%2)")
-            .arg(av_make_error_string(error, sizeof(error), ret))
-            .arg(gotpicture));
-        if (ret == AVERROR_INVALIDDATA)
-        {
-            if (++averror_count > SEQ_PKT_ERR_MAX)
+            if (ret == 0)
+                gotpicture = 1;
+            else
+                gotpicture = 0;
+            if (ret == AVERROR(EAGAIN))
+                ret = 0;
+            // If we got a picture do not send the packet until we have
+            // all available pictures
+            if (ret==0 && !gotpicture)
             {
-                // If erroring on GPU assist, try switching to software decode
-                if (codec_is_std(video_codec_id))
-                    m_parent->SetErrored(QObject::tr("Video Decode Error"));
+                ret2 = avcodec_send_packet(context, pkt);
+                if (ret2 == AVERROR(EAGAIN))
+                {
+                    tryAgain = true;
+                    ret2 = 0;
+                }
                 else
-                    m_streams_changed = true;
+                {
+                    sentPacket = true;
+                }
             }
         }
-        return false;
+        avcodeclock->unlock();
+
+        if (ret < 0 || ret2 < 0)
+        {
+            char error[AV_ERROR_MAX_STRING_SIZE];
+            if (ret < 0)
+            {
+                LOG(VB_GENERAL, LOG_ERR, LOC +
+                    QString("video avcodec_receive_frame error: %1 (%2) gotpicture:%3")
+                    .arg(av_make_error_string(error, sizeof(error), ret))
+                    .arg(ret).arg(gotpicture));
+            }
+            if (ret2 < 0)
+                LOG(VB_GENERAL, LOG_ERR, LOC +
+                    QString("video avcodec_send_packet error: %1 (%2) gotpicture:%3")
+                    .arg(av_make_error_string(error, sizeof(error), ret2))
+                    .arg(ret2).arg(gotpicture));
+            if (ret == AVERROR_INVALIDDATA || ret2 == AVERROR_INVALIDDATA)
+            {
+                if (++averror_count > SEQ_PKT_ERR_MAX)
+                {
+                    // If erroring on GPU assist, try switching to software decode
+                    if (codec_is_std(video_codec_id))
+                        m_parent->SetErrored(QObject::tr("Video Decode Error"));
+                    else
+                        m_streams_changed = true;
+                }
+            }
+            if (ret == AVERROR_EXTERNAL || ret2 == AVERROR_EXTERNAL)
+                m_streams_changed = true;
+            return false;
+        }
+
+        if (tryAgain)
+        {
+            if (++retryCount > PACKET_MAX_RETRIES)
+            {
+                LOG(VB_GENERAL, LOG_ERR, LOC +
+                    QString("ERROR: Video decode buffering retries exceeded maximum"));
+                return false;
+            }
+            LOG(VB_PLAYBACK, LOG_INFO, LOC +
+                QString("Video decode buffering retry"));
+            usleep(RETRY_WAIT_TIME);
+        }
     }
     // averror_count counts sequential errors, so if you have a successful
     // packet then reset it
     averror_count = 0;
-
-    if (!gotpicture)
+    if (gotpicture)
     {
-        return true;
-    }
+        LOG(VB_PLAYBACK | VB_TIMESTAMP, LOG_INFO, LOC +
+            QString("video timecodes packet-pts:%1 frame-pts:%2 packet-dts: %3 frame-dts:%4")
+                .arg(pkt->pts).arg(mpa_pic->pts).arg(pkt->pts)
+                .arg(mpa_pic->pkt_dts));
 
-    // Detect faulty video timestamps using logic from ffplay.
-    if (pkt->dts != (int64_t)AV_NOPTS_VALUE)
-    {
-        faulty_dts += (pkt->dts <= last_dts_for_fault_detection);
-        last_dts_for_fault_detection = pkt->dts;
-    }
-    if (mpa_pic->reordered_opaque != (int64_t)AV_NOPTS_VALUE)
-    {
-        faulty_pts += (mpa_pic->reordered_opaque <= last_pts_for_fault_detection);
-        last_pts_for_fault_detection = mpa_pic->reordered_opaque;
-        reordered_pts_detected = true;
-    }
+        if (!use_frame_timing)
+        {
+            // Detect faulty video timestamps using logic from ffplay.
+            if (pkt->dts != (int64_t)AV_NOPTS_VALUE)
+            {
+                faulty_dts += (pkt->dts <= last_dts_for_fault_detection);
+                last_dts_for_fault_detection = pkt->dts;
+            }
+            if (mpa_pic->reordered_opaque != (int64_t)AV_NOPTS_VALUE)
+            {
+                faulty_pts += (mpa_pic->reordered_opaque <= last_pts_for_fault_detection);
+                last_pts_for_fault_detection = mpa_pic->reordered_opaque;
+                reordered_pts_detected = true;
+            }
 
-    // Explicity use DTS for DVD since they should always be valid for every
-    // frame and fixups aren't enabled for DVD.
-    // Select reordered_opaque (PTS) timestamps if they are less faulty or the
-    // the DTS timestamp is missing. Also use fixups for missing PTS instead of
-    // DTS to avoid oscillating between PTS and DTS. Only select DTS if PTS is
-    // more faulty or never detected.
-    if (force_dts_timestamps)
-    {
-        if (pkt->dts != (int64_t)AV_NOPTS_VALUE)
-            pts = pkt->dts;
-        pts_selected = false;
-    }
-    else if (ringBuffer->IsDVD())
-    {
-        if (pkt->dts != (int64_t)AV_NOPTS_VALUE)
-            pts = pkt->dts;
-        pts_selected = false;
-    }
-    else if (private_dec && private_dec->NeedsReorderedPTS() &&
-             mpa_pic->reordered_opaque != (int64_t)AV_NOPTS_VALUE)
-    {
-        pts = mpa_pic->reordered_opaque;
-        pts_selected = true;
-    }
-    else if (faulty_pts <= faulty_dts && reordered_pts_detected)
-    {
-        if (mpa_pic->reordered_opaque != (int64_t)AV_NOPTS_VALUE)
-            pts = mpa_pic->reordered_opaque;
-        pts_selected = true;
+            // Explicity use DTS for DVD since they should always be valid for every
+            // frame and fixups aren't enabled for DVD.
+            // Select reordered_opaque (PTS) timestamps if they are less faulty or the
+            // the DTS timestamp is missing. Also use fixups for missing PTS instead of
+            // DTS to avoid oscillating between PTS and DTS. Only select DTS if PTS is
+            // more faulty or never detected.
+            if (force_dts_timestamps)
+            {
+                if (pkt->dts != (int64_t)AV_NOPTS_VALUE)
+                    pts = pkt->dts;
+                pts_selected = false;
+            }
+            else if (ringBuffer->IsDVD())
+            {
+                if (pkt->dts != (int64_t)AV_NOPTS_VALUE)
+                    pts = pkt->dts;
+                pts_selected = false;
+            }
+            else if (private_dec && private_dec->NeedsReorderedPTS() &&
+                    mpa_pic->reordered_opaque != (int64_t)AV_NOPTS_VALUE)
+            {
+                pts = mpa_pic->reordered_opaque;
+                pts_selected = true;
+            }
+            else if (faulty_pts <= faulty_dts && reordered_pts_detected)
+            {
+                if (mpa_pic->reordered_opaque != (int64_t)AV_NOPTS_VALUE)
+                    pts = mpa_pic->reordered_opaque;
+                pts_selected = true;
+            }
+            else if (pkt->dts != (int64_t)AV_NOPTS_VALUE)
+            {
+                pts = pkt->dts;
+                pts_selected = false;
+            }
+
+            LOG(VB_PLAYBACK | VB_TIMESTAMP, LOG_DEBUG, LOC +
+                QString("video packet timestamps reordered %1 pts %2 dts %3 (%4)")
+                    .arg(mpa_pic->reordered_opaque).arg(pkt->pts).arg(pkt->dts)
+                    .arg((force_dts_timestamps) ? "dts forced" :
+                        (pts_selected) ? "reordered" : "dts"));
+
+            mpa_pic->reordered_opaque = pts;
+        }
+        ProcessVideoFrame(curstream, mpa_pic);
     }
-    else if (pkt->dts != (int64_t)AV_NOPTS_VALUE)
+    if (!sentPacket)
     {
-        pts = pkt->dts;
-        pts_selected = false;
+        // MythTV logic expects that only one frame is processed
+        // Save the packet for later and return.
+        AVPacket *newPkt = new AVPacket;
+        memset(newPkt, 0, sizeof(AVPacket));
+        av_init_packet(newPkt);
+        av_packet_ref(newPkt, pkt);
+        storedPackets.prepend(newPkt);
     }
-
-    LOG(VB_PLAYBACK | VB_TIMESTAMP, LOG_DEBUG, LOC +
-        QString("video packet timestamps reordered %1 pts %2 dts %3 (%4)")
-            .arg(mpa_pic->reordered_opaque).arg(pkt->pts).arg(pkt->dts)
-            .arg((force_dts_timestamps) ? "dts forced" :
-                 (pts_selected) ? "reordered" : "dts"));
-
-    mpa_pic->reordered_opaque = pts;
-
-    ProcessVideoFrame(curstream, mpa_pic);
-
     return true;
 }
 
 bool AvFormatDecoder::ProcessVideoFrame(AVStream *stream, AVFrame *mpa_pic)
 {
+
+    if (flush_discard > 0)
+    {
+        flush_discard--;
+        return true;
+    }
+
     AVCodecContext *context = gCodecMap->getCodecContext(stream);
 
     // We need to mediate between ATSC and SCTE data when both are present.  If
@@ -3777,7 +3906,22 @@ bool AvFormatDecoder::ProcessVideoFrame(AVStream *stream, AVFrame *mpa_pic)
         return false;
     }
 
-    long long pts = (long long)(av_q2d(stream->time_base) *
+    long long pts;
+    if (use_frame_timing)
+    {
+        pts = mpa_pic->pts;
+        if (pts == AV_NOPTS_VALUE)
+            pts = mpa_pic->pkt_dts;
+        if (pts == AV_NOPTS_VALUE)
+        {
+            LOG(VB_GENERAL, LOG_ERR, LOC + "No PTS found - unable to process video.");
+            return false;
+        }
+        pts = (long long)(av_q2d(stream->time_base) *
+                                pts * 1000);
+    }
+    else
+        pts = (long long)(av_q2d(stream->time_base) *
                                 mpa_pic->reordered_opaque * 1000);
 
     long long temppts = pts;
@@ -3794,9 +3938,24 @@ bool AvFormatDecoder::ProcessVideoFrame(AVStream *stream, AVFrame *mpa_pic)
         temppts += (long long)(mpa_pic->repeat_pict * 500 / fps);
     }
 
+    // Calculate actual fps from the pts values.
+    long long ptsdiff = temppts - lastvpts;
+    double calcfps = 1000.0 / ptsdiff;
+    if (calcfps < 121.0 && calcfps > 3.0)
+    {
+        // If fps has doubled due to frame-doubling deinterlace
+        // Set fps to double value.
+        double fpschange = calcfps / fps;
+        if (fpschange > 1.9 && fpschange < 2.1)
+            fpsMultiplier = 2;
+        if (fpschange > 0.5 && fpschange < 0.6)
+            fpsMultiplier = 1;
+    }
+
     LOG(VB_PLAYBACK | VB_TIMESTAMP, LOG_INFO, LOC +
         QString("video timecode %1 %2 %3 %4%5")
-            .arg(mpa_pic->reordered_opaque).arg(pts).arg(temppts).arg(lastvpts)
+            .arg(use_frame_timing ? mpa_pic->pts : mpa_pic->reordered_opaque).arg(pts)
+            .arg(temppts).arg(lastvpts)
             .arg((pts != temppts) ? " fixup" : ""));
 
     if (picframe)
@@ -3815,7 +3974,11 @@ bool AvFormatDecoder::ProcessVideoFrame(AVStream *stream, AVFrame *mpa_pic)
 
     decoded_video_frame = picframe;
     gotVideoFrame = 1;
-    ++framesPlayed;
+    if (++fpsSkip >= fpsMultiplier)
+    {
+        ++framesPlayed;
+        fpsSkip = 0;
+    }
 
     lastvpts = temppts;
     if (!firstvpts && firstvptsinuse)
diff --git a/mythtv/libs/libmythtv/avformatdecoder.h b/mythtv/libs/libmythtv/avformatdecoder.h
index 486e0bb1bed..c3da8a6cedf 100644
--- a/mythtv/libs/libmythtv/avformatdecoder.h
+++ b/mythtv/libs/libmythtv/avformatdecoder.h
@@ -322,6 +322,10 @@ class AvFormatDecoder : public DecoderBase
     bool pts_detected;
     bool reordered_pts_detected;
     bool pts_selected;
+    // set use_frame_timing true to utilize the pts values in returned
+    // frames. Set fale to use deprecated method.
+    bool use_frame_timing;
+    int flush_discard;
 
     bool force_dts_timestamps;
 
diff --git a/mythtv/libs/libmythtv/decoderbase.cpp b/mythtv/libs/libmythtv/decoderbase.cpp
index 33b1676c60b..b8a2edef83b 100644
--- a/mythtv/libs/libmythtv/decoderbase.cpp
+++ b/mythtv/libs/libmythtv/decoderbase.cpp
@@ -20,6 +20,7 @@ DecoderBase::DecoderBase(MythPlayer *parent, const ProgramInfo &pginfo)
 
       current_width(640), current_height(480),
       current_aspect(1.33333), fps(29.97),
+      fpsMultiplier(1), fpsSkip(0),
       bitrate(4000),
 
       framesPlayed(0), framesRead(0),
@@ -80,6 +81,7 @@ void DecoderBase::Reset(bool reset_video_data, bool seek_reset, bool reset_file)
     {
         ResetPosMap();
         framesPlayed = 0;
+        fpsSkip = 0;
         framesRead = 0;
         totalDuration = AVRationalInit(0);
         dontSyncPositionMap = false;
@@ -581,6 +583,7 @@ bool DecoderBase::DoRewind(long long desiredFrame, bool discardFrames)
         return false;
 
     framesPlayed = lastKey;
+    fpsSkip = 0;
     framesRead = lastKey;
 
     // Do any Extra frame-by-frame seeking for exactseeks mode
@@ -880,6 +883,7 @@ void DecoderBase::DoFastForwardSeek(long long desiredFrame, bool &needflush)
         ringBuffer->Seek(e.pos, SEEK_SET);
         needflush    = true;
         framesPlayed = lastKey;
+        fpsSkip = 0;
         framesRead = lastKey;
     }
 }
diff --git a/mythtv/libs/libmythtv/decoderbase.h b/mythtv/libs/libmythtv/decoderbase.h
index 3f7eddb8ca5..608c1e6b336 100644
--- a/mythtv/libs/libmythtv/decoderbase.h
+++ b/mythtv/libs/libmythtv/decoderbase.h
@@ -269,6 +269,7 @@ class DecoderBase
     void SaveTotalFrames(void);
     bool GetVideoInverted(void) const { return video_inverted; }
     void TrackTotalDuration(bool track) { trackTotalDuration = track; }
+    int GetfpsMultiplier(void) { return fpsMultiplier; }
 
   protected:
     virtual int  AutoSelectTrack(uint type);
@@ -301,6 +302,8 @@ class DecoderBase
     int current_height;
     float current_aspect;
     double fps;
+    int fpsMultiplier;
+    int fpsSkip;
     uint bitrate;
 
     long long framesPlayed;
diff --git a/mythtv/libs/libmythtv/libmythtv.pro b/mythtv/libs/libmythtv/libmythtv.pro
index 39356bf774f..953fcb4a8b3 100644
--- a/mythtv/libs/libmythtv/libmythtv.pro
+++ b/mythtv/libs/libmythtv/libmythtv.pro
@@ -1,6 +1,7 @@
 include ( ../../settings.pro )
 
 QT += network xml sql widgets
+android: QT += androidextras
 
 TEMPLATE = lib
 TARGET = mythtv-$$LIBVERSION
@@ -503,6 +504,12 @@ using_frontend {
         using_opengl_video:DEFINES += USING_GLVAAPI
     }
 
+    using_mediacodec {
+        DEFINES += USING_MEDIACODEC
+        HEADERS += mediacodeccontext.h
+        SOURCES += mediacodeccontext.cpp
+    }
+
     # Misc. frontend
     HEADERS += DetectLetterbox.h
     SOURCES += DetectLetterbox.cpp
diff --git a/mythtv/libs/libmythtv/mediacodeccontext.cpp b/mythtv/libs/libmythtv/mediacodeccontext.cpp
new file mode 100644
index 00000000000..d95de6a01a1
--- /dev/null
+++ b/mythtv/libs/libmythtv/mediacodeccontext.cpp
@@ -0,0 +1,73 @@
+//////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 MythTV Developers <mythtv-dev@mythtv.org>
+//
+// This is part of MythTV (https://www.mythtv.org)
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+//////////////////////////////////////////////////////////////////////////////
+
+#include "mediacodeccontext.h"
+
+#include "mythlogging.h"
+
+extern "C" {
+    #include "libavutil/pixfmt.h"
+    #include "libavutil/hwcontext.h"
+    #include "libavcodec/avcodec.h"
+}
+
+#define LOC QString("MEDIACODEC: ")
+
+MythCodecID MediaCodecContext::GetBestSupportedCodec(
+    AVCodec **ppCodec,
+    const QString &decoder,
+    uint stream_type,
+    AVPixelFormat &pix_fmt)
+{
+    enum AVHWDeviceType type = AV_HWDEVICE_TYPE_MEDIACODEC;
+
+    AVPixelFormat fmt = AV_PIX_FMT_NONE;
+    if (decoder == "mediacodec")
+    {
+        QString decodername = QString((*ppCodec)->name) + "_mediacodec";
+        if (decodername == "mpeg2video_mediacodec")
+            decodername = "mpeg2_mediacodec";
+        AVCodec *newCodec = avcodec_find_decoder_by_name (decodername.toLocal8Bit());
+        if (newCodec)
+        {
+            *ppCodec = newCodec;
+            fmt = AV_PIX_FMT_MEDIACODEC;
+        }
+        else
+            LOG(VB_PLAYBACK, LOG_INFO, LOC +
+                QString("Decoder %1 does not support device type %2.")
+                    .arg((*ppCodec)->name).arg(av_hwdevice_get_type_name(type)));
+    }
+
+    if (fmt == AV_PIX_FMT_NONE)
+        return (MythCodecID)(kCodec_MPEG1 + (stream_type - 1));
+    else
+    {
+        LOG(VB_PLAYBACK, LOG_INFO, LOC +
+            QString("Decoder %1 supports device type %2.")
+                .arg((*ppCodec)->name).arg(av_hwdevice_get_type_name(type)));
+        pix_fmt = fmt;
+        return (MythCodecID)(kCodec_MPEG1_MEDIACODEC + (stream_type - 1));
+    }
+}
diff --git a/mythtv/libs/libmythtv/mediacodeccontext.h b/mythtv/libs/libmythtv/mediacodeccontext.h
new file mode 100644
index 00000000000..7f116487e0b
--- /dev/null
+++ b/mythtv/libs/libmythtv/mediacodeccontext.h
@@ -0,0 +1,41 @@
+//////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2017 MythTV Developers <mythtv-dev@mythtv.org>
+//
+// This is part of MythTV (https://www.mythtv.org)
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+//////////////////////////////////////////////////////////////////////////////
+
+
+#ifndef VIDEOOUTOPENGLMEDIACODEC_H
+#define VIDEOOUTOPENGLMEDIACODEC_H
+
+#include "videoout_opengl.h"
+
+
+class MediaCodecContext
+{
+  public:
+    static MythCodecID GetBestSupportedCodec(AVCodec **ppCodec,
+                                             const QString &decoder,
+                                             uint stream_type,
+                                             AVPixelFormat &pix_fmt);
+};
+
+#endif // VIDEOOUTOPENGLMEDIACODEC_H
\ No newline at end of file
diff --git a/mythtv/libs/libmythtv/mythcodecid.cpp b/mythtv/libs/libmythtv/mythcodecid.cpp
index cd9170a648d..b2023512b5f 100644
--- a/mythtv/libs/libmythtv/mythcodecid.cpp
+++ b/mythtv/libs/libmythtv/mythcodecid.cpp
@@ -102,6 +102,27 @@ QString toString(MythCodecID codecid)
         case kCodec_HEVC_DXVA2:
             return "HEVC DXVA2";
 
+        case kCodec_MPEG1_MEDIACODEC:
+            return "MPEG1 MEDIACODEC";
+        case kCodec_MPEG2_MEDIACODEC:
+            return "MPEG2 MEDIACODEC";
+        case kCodec_H263_MEDIACODEC:
+            return "H.263 MEDIACODEC";
+        case kCodec_MPEG4_MEDIACODEC:
+            return "MPEG4 MEDIACODEC";
+        case kCodec_H264_MEDIACODEC:
+            return "H.264 MEDIACODEC";
+        case kCodec_VC1_MEDIACODEC:
+            return "VC1 MEDIACODEC";
+        case kCodec_WMV3_MEDIACODEC:
+            return "WMV3 MEDIACODEC";
+        case kCodec_VP8_MEDIACODEC:
+            return "VP8 MEDIACODEC";
+        case kCodec_VP9_MEDIACODEC:
+            return "VP9 MEDIACODEC";
+        case kCodec_HEVC_MEDIACODEC:
+            return "HEVC MEDIACODEC";
+
         default:
             break;
     }
@@ -253,6 +274,37 @@ int myth2av_codecid(MythCodecID codec_id, bool &vdpau)
             ret = AV_CODEC_ID_HEVC;
             break;
 
+        case kCodec_MPEG1_MEDIACODEC:
+            ret = AV_CODEC_ID_MPEG1VIDEO;
+            break;
+        case kCodec_MPEG2_MEDIACODEC:
+            ret = AV_CODEC_ID_MPEG2VIDEO;
+            break;
+        case kCodec_H263_MEDIACODEC:
+            ret = AV_CODEC_ID_H263;
+            break;
+        case kCodec_MPEG4_MEDIACODEC:
+            ret = AV_CODEC_ID_MPEG4;
+            break;
+        case kCodec_H264_MEDIACODEC:
+            ret = AV_CODEC_ID_H264;
+            break;
+        case kCodec_VC1_MEDIACODEC:
+            ret = AV_CODEC_ID_VC1;
+            break;
+        case kCodec_WMV3_MEDIACODEC:
+            ret = AV_CODEC_ID_WMV3;
+            break;
+        case kCodec_VP8_MEDIACODEC:
+            ret = AV_CODEC_ID_VP8;
+            break;
+        case kCodec_VP9_MEDIACODEC:
+            ret = AV_CODEC_ID_VP9;
+            break;
+        case kCodec_HEVC_MEDIACODEC:
+            ret = AV_CODEC_ID_HEVC;
+            break;
+
         default:
             LOG(VB_GENERAL, LOG_ERR,
                 QString("Error: MythCodecID %1 has not been "
@@ -303,16 +355,19 @@ QString get_encoding_type(MythCodecID codecid)
         case kCodec_MPEG1_VDPAU:
         case kCodec_MPEG1_VAAPI:
         case kCodec_MPEG1_DXVA2:
+        case kCodec_MPEG1_MEDIACODEC:
         case kCodec_MPEG2:
         case kCodec_MPEG2_VDPAU:
         case kCodec_MPEG2_VAAPI:
         case kCodec_MPEG2_DXVA2:
+        case kCodec_MPEG2_MEDIACODEC:
             return "MPEG-2";
 
         case kCodec_H263:
         case kCodec_H263_VDPAU:
         case kCodec_H263_VAAPI:
         case kCodec_H263_DXVA2:
+        case kCodec_H263_MEDIACODEC:
             return "H.263";
 
         case kCodec_NUV_MPEG4:
@@ -320,42 +375,49 @@ QString get_encoding_type(MythCodecID codecid)
         case kCodec_MPEG4_VDPAU:
         case kCodec_MPEG4_VAAPI:
         case kCodec_MPEG4_DXVA2:
+        case kCodec_MPEG4_MEDIACODEC:
             return "MPEG-4";
 
         case kCodec_H264:
         case kCodec_H264_VDPAU:
         case kCodec_H264_VAAPI:
         case kCodec_H264_DXVA2:
+        case kCodec_H264_MEDIACODEC:
             return "H.264";
 
         case kCodec_VC1:
         case kCodec_VC1_VDPAU:
         case kCodec_VC1_VAAPI:
         case kCodec_VC1_DXVA2:
+        case kCodec_VC1_MEDIACODEC:
             return "VC-1";
 
         case kCodec_WMV3:
         case kCodec_WMV3_VDPAU:
         case kCodec_WMV3_VAAPI:
         case kCodec_WMV3_DXVA2:
+        case kCodec_WMV3_MEDIACODEC:
             return "WMV3";
 
         case kCodec_VP8:
         case kCodec_VP8_VDPAU:
         case kCodec_VP8_VAAPI:
         case kCodec_VP8_DXVA2:
+        case kCodec_VP8_MEDIACODEC:
             return "VP8";
 
         case kCodec_VP9:
         case kCodec_VP9_VDPAU:
         case kCodec_VP9_VAAPI:
         case kCodec_VP9_DXVA2:
+        case kCodec_VP9_MEDIACODEC:
             return "VP8";
 
         case kCodec_HEVC:
         case kCodec_HEVC_VDPAU:
         case kCodec_HEVC_VAAPI:
         case kCodec_HEVC_DXVA2:
+        case kCodec_HEVC_MEDIACODEC:
             return "HEVC";
 
         case kCodec_NONE:
@@ -363,6 +425,7 @@ QString get_encoding_type(MythCodecID codecid)
         case kCodec_VDPAU_END:
         case kCodec_VAAPI_END:
         case kCodec_DXVA2_END:
+        case kCodec_MEDIACODEC_END:
             return QString();
     }
 
@@ -380,5 +443,8 @@ QString get_decoder_name(MythCodecID codec_id)
     if (codec_is_dxva2(codec_id))
         return "dxva2";
 
+    if (codec_is_mediacodec(codec_id))
+        return "mediacodec";
+
     return "ffmpeg";
 }
diff --git a/mythtv/libs/libmythtv/mythcodecid.h b/mythtv/libs/libmythtv/mythcodecid.h
index 92466df9b8c..ce3620467d9 100644
--- a/mythtv/libs/libmythtv/mythcodecid.h
+++ b/mythtv/libs/libmythtv/mythcodecid.h
@@ -75,6 +75,22 @@ typedef enum
     kCodec_HEVC_DXVA2,
 
     kCodec_DXVA2_END,
+
+    kCodec_MEDIACODEC_BEGIN = kCodec_DXVA2_END,
+
+    kCodec_MPEG1_MEDIACODEC,
+    kCodec_MPEG2_MEDIACODEC,
+    kCodec_H263_MEDIACODEC,
+    kCodec_MPEG4_MEDIACODEC,
+    kCodec_H264_MEDIACODEC,
+    kCodec_VC1_MEDIACODEC,
+    kCodec_WMV3_MEDIACODEC,
+    kCodec_VP8_MEDIACODEC,
+    kCodec_VP9_MEDIACODEC,
+    kCodec_HEVC_MEDIACODEC,
+
+    kCodec_MEDIACODEC_END,
+
 } MythCodecID;
 
 // MythCodecID convenience functions
@@ -95,6 +111,10 @@ typedef enum
                                ((id == kCodec_H264_DXVA2)  ||   \
                                 (id == kCodec_MPEG2_DXVA2) ||   \
                                 (id == kCodec_VC1_DXVA2)))
+#define codec_is_mediacodec(id) ((id > kCodec_MEDIACODEC_BEGIN) &&     \
+                               (id < kCodec_MEDIACODEC_END))
+
+#define codec_sw_copy(id) (codec_is_std(id) || codec_is_mediacodec(id))
 
 QString get_encoding_type(MythCodecID codecid);
 QString get_decoder_name(MythCodecID codec_id);
@@ -130,8 +150,15 @@ int mpeg_version(int codec_id);
 #define CODEC_IS_DXVA2(codec, enc) (0)
 #endif
 
+#ifdef USING_MEDIACODEC
+#define CODEC_IS_MEDIACODEC(codec) (codec && (QString("mediacodec") == codec->wrapper_name))
+#else
+#define CODEC_IS_MEDIACODEC(codec) (0)
+#endif
+
 #define CODEC_IS_HWACCEL(codec, enc) (CODEC_IS_VDPAU(codec)      ||     \
                                       CODEC_IS_VAAPI(codec, enc) ||     \
-                                      CODEC_IS_DXVA2(codec, enc))
+                                      CODEC_IS_DXVA2(codec, enc) ||    \
+                                      CODEC_IS_MEDIACODEC(codec))
 
 #endif // _MYTH_CODEC_ID_H_
diff --git a/mythtv/libs/libmythtv/mythplayer.cpp b/mythtv/libs/libmythtv/mythplayer.cpp
index 1fa28968898..e4e0bd83ead 100644
--- a/mythtv/libs/libmythtv/mythplayer.cpp
+++ b/mythtv/libs/libmythtv/mythplayer.cpp
@@ -204,6 +204,7 @@ MythPlayer::MythPlayer(PlayerFlags flags)
       next_play_speed(1.0f),        next_normal_speed(true),
       play_speed(1.0f),             normal_speed(true),
       frame_interval((int)(1000000.0f / 30)), m_frame_interval(0),
+      m_fpsMultiplier(1),
       ffrew_skip(1),ffrew_adjust(0),
       // Audio and video synchronization stuff
       videosync(NULL),              avsync_delay(0),
@@ -1747,7 +1748,10 @@ int MythPlayer::NextCaptionTrack(int mode)
 
 void MythPlayer::SetFrameInterval(FrameScanType scan, double frame_period)
 {
-    frame_interval = (int)(1000000.0f * frame_period + 0.5f);
+    if (decoder)
+        m_fpsMultiplier = decoder->GetfpsMultiplier();
+    frame_interval = (int)(1000000.0f * frame_period + 0.5f)
+      / m_fpsMultiplier;
     if (!avsync_predictor_enabled)
         avsync_predictor = 0;
     avsync_predictor_enabled = false;
@@ -1923,7 +1927,7 @@ void MythPlayer::AVSync(VideoFrame *buffer, bool limit_delay)
         lastsync = true;
         //currentaudiotime = AVSyncGetAudiotime();
         LOG(VB_PLAYBACK, LOG_INFO, LOC + dbg + "dropping frame to catch up.");
-        if (!audio.IsPaused() && max_video_behind)
+        if (max_video_behind)
         {
             audio.Pause(true);
             avsync_audiopaused = true;
@@ -1940,8 +1944,8 @@ void MythPlayer::AVSync(VideoFrame *buffer, bool limit_delay)
         if (!player_ctx->IsPBP() || player_ctx->IsPrimaryPBP())
         {
             LOG(VB_PLAYBACK | VB_TIMESTAMP, LOG_INFO,
-                LOC + QString("AVSync waitforframe %1 %2")
-                    .arg(avsync_adjustment).arg(m_double_framerate));
+                LOC + QString("AVSync waitforframe %1 %2 %3")
+                    .arg(frameDelay).arg(avsync_adjustment).arg(m_double_framerate));
             vsync_delay_clock = videosync->WaitForFrame(frameDelay, avsync_adjustment + repeat_delay);
         }
         else
@@ -1979,7 +1983,12 @@ void MythPlayer::AVSync(VideoFrame *buffer, bool limit_delay)
             osdLock.unlock();
             // Display the second field
             if (!player_ctx->IsPBP() || player_ctx->IsPrimaryPBP())
+            {
+                LOG(VB_PLAYBACK | VB_TIMESTAMP, LOG_INFO,
+                    LOC + QString("AVSync waitforframe %1 %2 %3")
+                        .arg(frameDelay).arg(avsync_adjustment).arg(m_double_framerate));
                 vsync_delay_clock = videosync->WaitForFrame(frameDelay, avsync_adjustment);
+            }
             videoOutput->Show(ps);
         }
 
@@ -2199,11 +2208,8 @@ bool MythPlayer::PrebufferEnoughFrames(int min_buffers)
                 LOG(VB_PLAYBACK, LOG_NOTICE, LOC +
                     QString("Pause to allow live tv catch up. Position in sec. Current: %2, Total: %3")
                     .arg(current).arg(length));
-                if (!audio.IsPaused())
-                {
-                    audio.Pause(true);
-                    avsync_audiopaused = true;
-                }
+                audio.Pause(true);
+                avsync_audiopaused = true;
                 prebufferFramesPlayed = framesPlayed;
             }
         }
@@ -2304,6 +2310,9 @@ void MythPlayer::DisplayNormalFrame(bool check_prebuffer)
     // Check aspect ratio
     CheckAspectRatio(frame);
 
+    if (decoder && m_fpsMultiplier != decoder->GetfpsMultiplier())
+        UpdateFFRewSkip();
+
     // Player specific processing (dvd, bd, mheg etc)
     PreProcessNormalFrame();
 
@@ -3737,7 +3746,10 @@ bool MythPlayer::UpdateFFRewSkip(void)
     if (play_speed >= 0.0f && play_speed <= 3.0f)
     {
         skip_changed = (ffrew_skip != 1);
-        frame_interval = (int) (1000000.0f / video_frame_rate / temp_speed);
+        if (decoder)
+            m_fpsMultiplier = decoder->GetfpsMultiplier();
+        frame_interval = (int) (1000000.0f / video_frame_rate / temp_speed)
+          / m_fpsMultiplier;
         ffrew_skip = (play_speed != 0.0f);
     }
     else
diff --git a/mythtv/libs/libmythtv/mythplayer.h b/mythtv/libs/libmythtv/mythplayer.h
index 7fe3bb3cec4..a17364522be 100644
--- a/mythtv/libs/libmythtv/mythplayer.h
+++ b/mythtv/libs/libmythtv/mythplayer.h
@@ -810,6 +810,7 @@ class MTV_PUBLIC MythPlayer
     bool       normal_speed;
     int        frame_interval;///< always adjusted for play_speed
     int        m_frame_interval;///< used to detect changes to frame_interval
+    int        m_fpsMultiplier;///< used to detect changes
 
     int        ffrew_skip;
     int        ffrew_adjust;
diff --git a/mythtv/libs/libmythtv/videobuffers.cpp b/mythtv/libs/libmythtv/videobuffers.cpp
index e20c13d2772..92620924ca9 100644
--- a/mythtv/libs/libmythtv/videobuffers.cpp
+++ b/mythtv/libs/libmythtv/videobuffers.cpp
@@ -15,9 +15,9 @@ extern "C" {
 #include "compat.h"
 #include "mythlogging.h"
 
-#define TRY_LOCK_SPINS                 100
-#define TRY_LOCK_SPINS_BEFORE_WARNING   10
-#define TRY_LOCK_SPIN_WAIT             100 /* usec */
+#define TRY_LOCK_SPINS                 2000
+#define TRY_LOCK_SPINS_BEFORE_WARNING  9999
+#define TRY_LOCK_SPIN_WAIT             1000 /* usec */
 
 int next_dbg_str = 0;
 
@@ -289,6 +289,9 @@ VideoFrame *VideoBuffers::GetNextFreeFrame(BufferType enqueue_to)
 
         if (tries >= TRY_LOCK_SPINS)
         {
+            LOG(VB_GENERAL, LOG_ERR, QString("GetNextFreeFrame: "
+            "available:%1 used:%2 limbo:%3 pause:%4 displayed:%5 decode:%6 finished:%7")
+            .arg(available.size()).arg(used.size()).arg(limbo.size()).arg(pause.size()).arg(displayed.size()).arg(decode.size()).arg(finished.size()));
             LOG(VB_GENERAL, LOG_ERR,
                 QString("GetNextFreeFrame() unable to "
                         "lock frame %1 times. Discarding Frames.")
diff --git a/mythtv/libs/libmythtv/videodisplayprofile.cpp b/mythtv/libs/libmythtv/videodisplayprofile.cpp
index 4aec35f421d..700ee3bb990 100644
--- a/mythtv/libs/libmythtv/videodisplayprofile.cpp
+++ b/mythtv/libs/libmythtv/videodisplayprofile.cpp
@@ -851,6 +851,7 @@ QString VideoDisplayProfile::GetDecoderName(const QString &decoder)
         dec_name["vaapi"]    = QObject::tr("VAAPI acceleration");
         dec_name["dxva2"]    = QObject::tr("Windows hardware acceleration");
         dec_name["vda"]      = QObject::tr("Mac VDA hardware acceleration");
+        dec_name["mediacodec"] = QObject::tr("Android MediaCodec decoder");
     }
 
     QString ret = decoder;
@@ -907,6 +908,11 @@ QString VideoDisplayProfile::GetDecoderHelp(QString decoder)
             "Openmax will use the graphics hardware to "
             "accelerate video decoding on Raspberry Pi. ");
 
+    if (decoder == "mediacodec")
+        msg += QObject::tr(
+            "Mediacodec will use the graphics hardware to "
+            "accelerate video decoding on Android. ");
+
     return msg;
 }
 
@@ -1453,6 +1459,20 @@ void VideoDisplayProfile::CreateProfiles(const QString &hostname)
                       "");
     }
 #endif
+
+#ifdef USING_MEDIACODEC
+    if (!profiles.contains("MediaCodec Normal")) {
+        (void) QObject::tr("MediaCodec Normal",
+                           "Sample: MediaCodec Normal");
+        groupid = CreateProfileGroup("MediaCodec Normal", hostname);
+        CreateProfile(groupid, 1, "", "", "",
+                      "mediacodec", 4, true, "opengl",
+                      "opengl2", true,
+                      "none", "none",
+                      "");
+    }
+#endif
+
 }
 
 QStringList VideoDisplayProfile::GetVideoRenderers(const QString &decoder)
diff --git a/mythtv/libs/libmythtv/videoout_opengl.cpp b/mythtv/libs/libmythtv/videoout_opengl.cpp
index af80727f7ef..b689947d738 100644
--- a/mythtv/libs/libmythtv/videoout_opengl.cpp
+++ b/mythtv/libs/libmythtv/videoout_opengl.cpp
@@ -38,6 +38,8 @@ void VideoOutputOpenGL::GetRenderOptions(render_opts &opts,
         (*opts.safe_renderers)["crystalhd"].append("opengl");
     if (opts.decoders->contains("openmax"))
         (*opts.safe_renderers)["openmax"].append("opengl");
+    if (opts.decoders->contains("mediacodec"))
+        (*opts.safe_renderers)["mediacodec"].append("opengl");
     opts.priorities->insert("opengl", 65);
 
     // lite profile - no colourspace control, GPU deinterlacing
@@ -268,7 +270,7 @@ bool VideoOutputOpenGL::InputChanged(const QSize &video_dim_buf,
         StopEmbedding();
     }
 
-    if (!codec_is_std(av_codec_id))
+    if (!codec_is_std(av_codec_id) && !codec_is_mediacodec(av_codec_id))
     {
         LOG(VB_GENERAL, LOG_ERR, LOC + "New video codec is not supported.");
         errorState = kError_Unknown;
@@ -417,7 +419,7 @@ bool VideoOutputOpenGL::SetupOpenGL(void)
                                   window.GetVideoDispDim(), dvr,
                                   window.GetDisplayVideoRect(),
                                   window.GetVideoRect(), true,
-                                  options, !codec_is_std(video_codec_id));
+                                  options, !codec_sw_copy(video_codec_id));
     if (success)
     {
         bool temp_deinterlacing = m_deinterlacing;
@@ -473,7 +475,11 @@ void VideoOutputOpenGL::CreatePainter(void)
 bool VideoOutputOpenGL::CreateBuffers(void)
 {
     QMutexLocker locker(&gl_context_lock);
-    vbuffers.Init(31, true, 1, 12, 4, 2);
+    if (codec_is_mediacodec(video_codec_id))
+        // vbuffers.Init(4, true, 1, 2, 2, 1);
+        vbuffers.Init(8, true, 1, 4, 2, 1);
+    else
+        vbuffers.Init(31, true, 1, 12, 4, 2);
     return vbuffers.CreateBuffers(FMT_YV12,
                                   window.GetVideoDim().width(),
                                   window.GetVideoDim().height());
@@ -525,7 +531,7 @@ void VideoOutputOpenGL::ProcessFrame(VideoFrame *frame, OSD */*osd*/,
         gl_valid = true;
     }
 
-    bool sw_frame = codec_is_std(video_codec_id) &&
+    bool sw_frame = codec_sw_copy(video_codec_id) &&
                     video_codec_id != kCodec_NONE;
     bool deint_proc = m_deinterlacing && (m_deintFilter != NULL);
     OpenGLLocker ctx_lock(gl_context);
@@ -735,6 +741,10 @@ QStringList VideoOutputOpenGL::GetAllowedRenderers(
     {
         list << "opengl" << "opengl-lite";
     }
+    else if (codec_is_mediacodec(myth_codec_id) && !getenv("NO_OPENGL"))
+    {
+        list << "opengl";
+    }
 
     return list;
 }
