tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

gmp-fake-openh264.cpp (14022B)


      1 /*!
      2 * \copy
      3 *     Copyright (c)  2009-2014, Cisco Systems
      4 *     Copyright (c)  2014, Mozilla
      5 *     All rights reserved.
      6 *
      7 *     Redistribution and use in source and binary forms, with or without
      8 *     modification, are permitted provided that the following conditions
      9 *     are met:
     10 *
     11 *        * Redistributions of source code must retain the above copyright
     12 *          notice, this list of conditions and the following disclaimer.
     13 *
     14 *        * Redistributions in binary form must reproduce the above copyright
     15 *          notice, this list of conditions and the following disclaimer in
     16 *          the documentation and/or other materials provided with the
     17 *          distribution.
     18 *
     19 *     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     20 *     "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     21 *     LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
     22 *     FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
     23 *     COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
     24 *     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     25 *     BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     26 *     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
     27 *     CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     28 *     LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
     29 *     ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     30 *     POSSIBILITY OF SUCH DAMAGE.
     31 *
     32 *
     33 *************************************************************************************
     34 */
     35 
     36 #include <assert.h>
     37 #include <stdint.h>
     38 
     39 #include <cstring>
     40 #include <iostream>
     41 
     42 #include "gmp-platform.h"
     43 #include "gmp-video-decode.h"
     44 #include "gmp-video-encode.h"
     45 #include "gmp-video-frame-encoded.h"
     46 #include "gmp-video-frame-i420.h"
     47 #include "gmp-video-host.h"
     48 #include "mozilla/PodOperations.h"
     49 
     50 #if defined(_MSC_VER)
     51 #  define PUBLIC_FUNC __declspec(dllexport)
     52 #else
     53 #  define PUBLIC_FUNC
     54 #endif
     55 
     56 #define BIG_FRAME 10000
     57 
     58 #define GL_CRIT 0
     59 #define GL_ERROR 1
     60 #define GL_INFO 2
     61 #define GL_DEBUG 3
     62 
     63 const char* kLogStrings[] = {"Critical", "Error", "Info", "Debug"};
     64 
     65 static int g_log_level = GL_CRIT;
     66 
     67 #define GMPLOG(l, x)                                \
     68  do {                                              \
     69    if (l <= g_log_level) {                         \
     70      const char* log_string = "unknown";           \
     71      if ((l >= 0) && (l <= 3)) {                   \
     72        log_string = kLogStrings[l];                \
     73      }                                             \
     74      std::cerr << log_string << ": " << x << '\n'; \
     75    }                                               \
     76  } while (0)
     77 
     78 class FakeVideoEncoder;
     79 class FakeVideoDecoder;
     80 
     81 // Turn off padding for this structure. Having extra bytes can result in
     82 // bitstream parsing problems.
     83 #pragma pack(push, 1)
     84 struct EncodedFrame {
     85  struct SPSNalu {
     86    uint32_t size_;
     87    uint8_t payload[14];
     88  } sps_nalu;
     89  struct PPSNalu {
     90    uint32_t size_;
     91    uint8_t payload[4];
     92  } pps_nalu;
     93  struct IDRNalu {
     94    uint32_t size_;
     95    uint8_t h264_compat_;
     96    uint32_t magic_;
     97    uint32_t width_;
     98    uint32_t height_;
     99    uint8_t y_;
    100    uint8_t u_;
    101    uint8_t v_;
    102    uint64_t timestamp_;
    103  } idr_nalu;
    104 };
    105 #pragma pack(pop)
    106 
    107 #define ENCODED_FRAME_MAGIC 0x004000b8
    108 
    109 template <typename T>
    110 class SelfDestruct {
    111 public:
    112  explicit SelfDestruct(T* t) : t_(t) {}
    113  ~SelfDestruct() {
    114    if (t_) {
    115      t_->Destroy();
    116    }
    117  }
    118 
    119 private:
    120  T* t_;
    121 };
    122 
    123 class FakeVideoEncoder : public GMPVideoEncoder {
    124 public:
    125  explicit FakeVideoEncoder(GMPVideoHost* hostAPI) : host_(hostAPI) {}
    126 
    127  void InitEncode(const GMPVideoCodec& codecSettings,
    128                  const uint8_t* aCodecSpecific, uint32_t aCodecSpecificSize,
    129                  GMPVideoEncoderCallback* callback, int32_t numberOfCores,
    130                  uint32_t maxPayloadSize) override {
    131    callback_ = callback;
    132    frame_size_ = (maxPayloadSize > 0 && maxPayloadSize < BIG_FRAME)
    133                      ? maxPayloadSize
    134                      : BIG_FRAME;
    135    frame_size_ -= 24 + 40;
    136    // default header+extension size is 24, but let's leave extra room if
    137    // we enable more extensions.
    138    // XXX -- why isn't the size passed in based on the size minus extensions?
    139 
    140    const char* env = getenv("GMP_LOGGING");
    141    if (env) {
    142      g_log_level = atoi(env);
    143    }
    144    GMPLOG(GL_INFO, "Initialized encoder");
    145  }
    146 
    147  void SendFrame(GMPVideoi420Frame* inputImage, GMPVideoFrameType frame_type,
    148                 int nal_type) {
    149    // Encode this in a frame that looks a little bit like H.264.
    150    // Send SPS/PPS/IDR to avoid confusing people
    151    // Copy the data. This really should convert this to network byte order.
    152    EncodedFrame eframe;
    153 
    154    // These values were chosen to force a SPS id of 0
    155    eframe.sps_nalu = {sizeof(EncodedFrame::SPSNalu) - sizeof(uint32_t),
    156                       {0x67, 0x42, 0xc0, 0xd, 0x8c, 0x8d, 0x40, 0xa0, 0xf9,
    157                        0x0, 0xf0, 0x88, 0x46, 0xa0}};
    158 
    159    // These values were chosen to force a PPS id of 0
    160    eframe.pps_nalu = {sizeof(EncodedFrame::PPSNalu) - sizeof(uint32_t),
    161                       {0x68, 0xce, 0x3c, 0x80}};
    162 
    163    eframe.idr_nalu.size_ = sizeof(EncodedFrame::IDRNalu) - sizeof(uint32_t);
    164    // We force IFrame here - if we send PFrames, the webrtc.org code gets
    165    // tripped up attempting to find non-existent previous IFrames.
    166    eframe.idr_nalu.h264_compat_ =
    167        nal_type;  // 5 = IFrame/IDR slice, 1=PFrame/slice
    168    eframe.idr_nalu.magic_ = ENCODED_FRAME_MAGIC;
    169    eframe.idr_nalu.width_ = inputImage->Width();
    170    eframe.idr_nalu.height_ = inputImage->Height();
    171    eframe.idr_nalu.y_ = AveragePlane(inputImage->Buffer(kGMPYPlane),
    172                                      inputImage->AllocatedSize(kGMPYPlane));
    173    eframe.idr_nalu.u_ = AveragePlane(inputImage->Buffer(kGMPUPlane),
    174                                      inputImage->AllocatedSize(kGMPUPlane));
    175    eframe.idr_nalu.v_ = AveragePlane(inputImage->Buffer(kGMPVPlane),
    176                                      inputImage->AllocatedSize(kGMPVPlane));
    177 
    178    eframe.idr_nalu.timestamp_ = inputImage->Timestamp();
    179 
    180    // Now return the encoded data back to the parent.
    181    GMPVideoFrame* ftmp;
    182    GMPErr err = host_->CreateFrame(kGMPEncodedVideoFrame, &ftmp);
    183    if (err != GMPNoErr) {
    184      GMPLOG(GL_ERROR, "Error creating encoded frame");
    185      return;
    186    }
    187 
    188    GMPVideoEncodedFrame* f = static_cast<GMPVideoEncodedFrame*>(ftmp);
    189 
    190    err = f->CreateEmptyFrame(sizeof(eframe));
    191    if (err != GMPNoErr) {
    192      GMPLOG(GL_ERROR, "Error allocating frame data");
    193      f->Destroy();
    194      return;
    195    }
    196    memcpy(f->Buffer(), &eframe, sizeof(eframe));
    197    f->SetEncodedWidth(eframe.idr_nalu.width_);
    198    f->SetEncodedHeight(eframe.idr_nalu.height_);
    199    f->SetTimeStamp(eframe.idr_nalu.timestamp_);
    200    f->SetFrameType(frame_type);
    201    f->SetCompleteFrame(true);
    202    f->SetBufferType(GMP_BufferLength32);
    203 
    204    GMPLOG(GL_DEBUG, "Encoding complete. type= "
    205                         << f->FrameType()
    206                         << " NAL_type=" << (int)eframe.idr_nalu.h264_compat_
    207                         << " length=" << f->Size()
    208                         << " timestamp=" << f->TimeStamp()
    209                         << " width/height=" << eframe.idr_nalu.width_ << "x"
    210                         << eframe.idr_nalu.height_);
    211 
    212    // Return the encoded frame.
    213    GMPCodecSpecificInfo info;
    214    mozilla::PodZero(&info);
    215    info.mCodecType = kGMPVideoCodecH264;
    216    info.mBufferType = GMP_BufferLength32;
    217    info.mCodecSpecific.mH264.mSimulcastIdx = 0;
    218    GMPLOG(GL_DEBUG, "Calling callback");
    219    callback_->Encoded(f, reinterpret_cast<uint8_t*>(&info), sizeof(info));
    220    GMPLOG(GL_DEBUG, "Callback called");
    221  }
    222 
    223  void Encode(GMPVideoi420Frame* inputImage, const uint8_t* aCodecSpecificInfo,
    224              uint32_t aCodecSpecificInfoLength,
    225              const GMPVideoFrameType* aFrameTypes,
    226              uint32_t aFrameTypesLength) override {
    227    GMPLOG(GL_DEBUG, __FUNCTION__ << " size=" << inputImage->Width() << "x"
    228                                  << inputImage->Height());
    229 
    230    assert(aFrameTypesLength != 0);
    231    GMPVideoFrameType frame_type = aFrameTypes[0];
    232 
    233    SelfDestruct<GMPVideoi420Frame> ifd(inputImage);
    234 
    235    if (frame_type == kGMPKeyFrame) {
    236      if (!inputImage) return;
    237    }
    238    if (!inputImage) {
    239      GMPLOG(GL_ERROR, "no input image");
    240      return;
    241    }
    242 
    243    if (frame_type == kGMPKeyFrame ||
    244        frames_encoded_++ % 10 == 0) {  // periodically send iframes anyways
    245      // 5 = IFrame/IDR slice
    246      SendFrame(inputImage, kGMPKeyFrame, 5);
    247    } else {
    248      // 1 = PFrame/slice
    249      SendFrame(inputImage, frame_type, 1);
    250    }
    251  }
    252 
    253  void SetChannelParameters(uint32_t aPacketLoss, uint32_t aRTT) override {}
    254 
    255  void SetRates(uint32_t aNewBitRate, uint32_t aFrameRate) override {}
    256 
    257  void SetPeriodicKeyFrames(bool aEnable) override {}
    258 
    259  void EncodingComplete() override { delete this; }
    260 
    261 private:
    262  uint8_t AveragePlane(uint8_t* ptr, size_t len) {
    263    uint64_t val = 0;
    264 
    265    for (size_t i = 0; i < len; ++i) {
    266      val += ptr[i];
    267    }
    268 
    269    return (val / len) % 0xff;
    270  }
    271 
    272  GMPVideoHost* host_;
    273  GMPVideoEncoderCallback* callback_ = nullptr;
    274  uint32_t frame_size_ = BIG_FRAME;
    275  uint32_t frames_encoded_ = 0;
    276 };
    277 
    278 class FakeVideoDecoder : public GMPVideoDecoder {
    279 public:
    280  explicit FakeVideoDecoder(GMPVideoHost* hostAPI)
    281      : host_(hostAPI), callback_(nullptr) {}
    282 
    283  ~FakeVideoDecoder() override = default;
    284 
    285  void InitDecode(const GMPVideoCodec& codecSettings,
    286                  const uint8_t* aCodecSpecific, uint32_t aCodecSpecificSize,
    287                  GMPVideoDecoderCallback* callback,
    288                  int32_t coreCount) override {
    289    GMPLOG(GL_INFO, "InitDecode");
    290 
    291    const char* env = getenv("GMP_LOGGING");
    292    if (env) {
    293      g_log_level = atoi(env);
    294    }
    295    callback_ = callback;
    296  }
    297 
    298  void Decode(GMPVideoEncodedFrame* inputFrame, bool missingFrames,
    299              const uint8_t* aCodecSpecificInfo,
    300              uint32_t aCodecSpecificInfoLength,
    301              int64_t renderTimeMs = -1) override {
    302    GMPLOG(GL_DEBUG, __FUNCTION__
    303                         << "Decoding frame size=" << inputFrame->Size()
    304                         << " timestamp=" << inputFrame->TimeStamp());
    305 
    306    // Attach a self-destructor so that the input frame is destroyed on return.
    307    SelfDestruct<GMPVideoEncodedFrame> ifd(inputFrame);
    308 
    309    EncodedFrame* eframe;
    310    eframe = reinterpret_cast<EncodedFrame*>(inputFrame->Buffer());
    311    GMPLOG(GL_DEBUG, "magic=" << eframe->idr_nalu.magic_ << " h264_compat="
    312                              << (int)eframe->idr_nalu.h264_compat_
    313                              << " width=" << eframe->idr_nalu.width_
    314                              << " height=" << eframe->idr_nalu.height_
    315                              << " timestamp=" << inputFrame->TimeStamp()
    316                              << " y/u/v=" << (int)eframe->idr_nalu.y_ << ":"
    317                              << (int)eframe->idr_nalu.u_ << ":"
    318                              << (int)eframe->idr_nalu.v_);
    319    if (inputFrame->Size() != (sizeof(*eframe))) {
    320      GMPLOG(GL_ERROR, "Couldn't decode frame. Size=" << inputFrame->Size());
    321      return;
    322    }
    323 
    324    if (eframe->idr_nalu.magic_ != ENCODED_FRAME_MAGIC) {
    325      GMPLOG(GL_ERROR,
    326             "Couldn't decode frame. Magic=" << eframe->idr_nalu.magic_);
    327      return;
    328    }
    329    if (eframe->idr_nalu.h264_compat_ != 5 &&
    330        eframe->idr_nalu.h264_compat_ != 1) {
    331      // only return video for iframes or pframes
    332      GMPLOG(GL_DEBUG, "Not a video frame: NAL type "
    333                           << (int)eframe->idr_nalu.h264_compat_);
    334      return;
    335    }
    336 
    337    int width = eframe->idr_nalu.width_;
    338    int height = eframe->idr_nalu.height_;
    339    int ystride = eframe->idr_nalu.width_;
    340    // Round up so the data fits, or CreateEmptyFrame will fail on odd width and
    341    // height.
    342    int uvstride = (ystride + (ystride % 2)) / 2;
    343 
    344    GMPLOG(GL_DEBUG, "Video frame ready for display "
    345                         << width << "x" << height
    346                         << " timestamp=" << inputFrame->TimeStamp());
    347 
    348    GMPVideoFrame* ftmp = nullptr;
    349 
    350    // Translate the image.
    351    GMPErr err = host_->CreateFrame(kGMPI420VideoFrame, &ftmp);
    352    if (err != GMPNoErr) {
    353      GMPLOG(GL_ERROR, "Couldn't allocate empty I420 frame");
    354      return;
    355    }
    356 
    357    GMPVideoi420Frame* frame = static_cast<GMPVideoi420Frame*>(ftmp);
    358    err = frame->CreateEmptyFrame(width, height, ystride, uvstride, uvstride);
    359    if (err != GMPNoErr) {
    360      GMPLOG(GL_ERROR, "Couldn't make decoded frame");
    361      return;
    362    }
    363 
    364    memset(frame->Buffer(kGMPYPlane), eframe->idr_nalu.y_,
    365           frame->AllocatedSize(kGMPYPlane));
    366    memset(frame->Buffer(kGMPUPlane), eframe->idr_nalu.u_,
    367           frame->AllocatedSize(kGMPUPlane));
    368    memset(frame->Buffer(kGMPVPlane), eframe->idr_nalu.v_,
    369           frame->AllocatedSize(kGMPVPlane));
    370 
    371    GMPLOG(GL_DEBUG, "Allocated size = " << frame->AllocatedSize(kGMPYPlane));
    372    frame->SetTimestamp(inputFrame->TimeStamp());
    373    frame->SetDuration(inputFrame->Duration());
    374    callback_->Decoded(frame);
    375  }
    376 
    377  void Reset() override {}
    378 
    379  void Drain() override {}
    380 
    381  void DecodingComplete() override { delete this; }
    382 
    383  GMPVideoHost* host_;
    384  GMPVideoDecoderCallback* callback_;
    385 };
    386 
    387 extern "C" {
    388 
    389 PUBLIC_FUNC GMPErr GMPInit(const GMPPlatformAPI* aPlatformAPI) {
    390  return GMPNoErr;
    391 }
    392 
    393 PUBLIC_FUNC GMPErr GMPGetAPI(const char* aApiName, void* aHostAPI,
    394                             void** aPluginApi) {
    395  if (!strcmp(aApiName, GMP_API_VIDEO_DECODER)) {
    396    *aPluginApi = new FakeVideoDecoder(static_cast<GMPVideoHost*>(aHostAPI));
    397    return GMPNoErr;
    398  }
    399  if (!strcmp(aApiName, GMP_API_VIDEO_ENCODER)) {
    400    *aPluginApi = new FakeVideoEncoder(static_cast<GMPVideoHost*>(aHostAPI));
    401    return GMPNoErr;
    402  }
    403  return GMPGenericErr;
    404 }
    405 
    406 PUBLIC_FUNC void GMPShutdown(void) {}
    407 
    408 }  // extern "C"