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"