--- a/dom/media/gmp-plugin-openh264/gmp-fake-openh264.cpp
+++ b/dom/media/gmp-plugin-openh264/gmp-fake-openh264.cpp
@@ -80,34 +80,38 @@ static int g_log_level = GL_CRIT;
if ((l >= 0) && (l <= 3)) { \
log_string = kLogStrings[l]; \
} \
std::cerr << log_string << ": " << x << std::endl; \
} \
} while(0)
-GMPPlatformAPI* g_platform_api = nullptr;
-
class FakeVideoEncoder;
class FakeVideoDecoder;
struct EncodedFrame {
uint32_t length_;
- uint8_t h264_compat_;
- uint32_t magic_;
uint32_t width_;
uint32_t height_;
uint8_t y_;
uint8_t u_;
uint8_t v_;
uint32_t timestamp_;
};
-#define ENCODED_FRAME_MAGIC 0x4652414d
+// Canned H264 stuff based on the real encoder's output.
+static const uint8_t cannedSps[] = {0x67, 0x42, 0xc0, 0x29,
+ 0x8c, 0x8d, 0x40, 0xa0,
+ 0xf9, 0x00, 0xf0, 0x88,
+ 0x46, 0xa0};
+
+static const uint8_t cannedPps[] = {0x68, 0xce, 0x3c, 0x80};
+static const uint8_t cannedIFramePrefix[] = {0x65, 0xb8};
+static const uint8_t cannedPFramePrefix[] = {0x61, 0xe0};
template <typename T> class SelfDestruct {
public:
explicit SelfDestruct (T* t) : t_ (t) {}
~SelfDestruct() {
if (t_) {
t_->Destroy();
}
@@ -121,31 +125,16 @@ template <typename T> class SelfDestruct
return t;
}
#endif
private:
T* t_;
};
-class FakeEncoderTask : public GMPTask {
- public:
- FakeEncoderTask(FakeVideoEncoder* encoder,
- GMPVideoi420Frame* frame,
- GMPVideoFrameType type)
- : encoder_(encoder), frame_(frame), type_(type) {}
-
- void Run() override;
- void Destroy() override { delete this; }
-
- FakeVideoEncoder* encoder_;
- GMPVideoi420Frame* frame_;
- GMPVideoFrameType type_;
-};
-
class FakeVideoEncoder : public GMPVideoEncoder {
public:
explicit FakeVideoEncoder (GMPVideoHost* hostAPI) :
host_ (hostAPI),
callback_ (nullptr),
frame_size_(BIG_FRAME),
frames_encoded_(0) {}
@@ -169,75 +158,110 @@ class FakeVideoEncoder : public GMPVideo
}
GMPLOG (GL_INFO, "Initialized encoder");
}
void SendFrame(GMPVideoi420Frame* inputImage,
GMPVideoFrameType frame_type,
uint8_t nal_type)
{
- // Encode this in a frame that looks a little bit like H.264.
- // Send SPS/PPS/IDR to avoid confusing people
- // Copy the data. This really should convert this to network byte order.
- EncodedFrame eframe;
- eframe.length_ = sizeof(eframe) - sizeof(uint32_t);
- eframe.h264_compat_ = nal_type; // 7 = SPS, 8 = PPS, 5 = IFrame/IDR slice, 1=PFrame/slice
- eframe.magic_ = ENCODED_FRAME_MAGIC;
- eframe.width_ = inputImage->Width();
- eframe.height_ = inputImage->Height();
- eframe.y_ = AveragePlane(inputImage->Buffer(kGMPYPlane),
- inputImage->AllocatedSize(kGMPYPlane));
- eframe.u_ = AveragePlane(inputImage->Buffer(kGMPUPlane),
- inputImage->AllocatedSize(kGMPUPlane));
- eframe.v_ = AveragePlane(inputImage->Buffer(kGMPVPlane),
- inputImage->AllocatedSize(kGMPVPlane));
-
- eframe.timestamp_ = inputImage->Timestamp();
-
- // Now return the encoded data back to the parent.
GMPVideoFrame* ftmp;
GMPErr err = host_->CreateFrame(kGMPEncodedVideoFrame, &ftmp);
if (err != GMPNoErr) {
GMPLOG (GL_ERROR, "Error creating encoded frame");
return;
}
GMPVideoEncodedFrame* f = static_cast<GMPVideoEncodedFrame*> (ftmp);
- err = f->CreateEmptyFrame (sizeof(eframe) +
- (nal_type == 5 ? sizeof(uint32_t) + frame_size_ : 0));
+ uint32_t length = 0;
+ switch (nal_type) {
+ case 8:
+ length = sizeof(cannedPps);
+ break;
+ case 7:
+ length = sizeof(cannedSps);
+ break;
+ case 5:
+ length = frame_size_;
+ break;
+ case 1:
+ length = sizeof(cannedPFramePrefix) + sizeof(EncodedFrame);
+ break;
+ default:
+ MOZ_CRASH();
+ }
+
+ err = f->CreateEmptyFrame (length + sizeof(uint32_t));
if (err != GMPNoErr) {
GMPLOG (GL_ERROR, "Error allocating frame data");
f->Destroy();
return;
}
- memcpy(f->Buffer(), &eframe, sizeof(eframe));
- if (nal_type == 5) {
- // set the size for the fake iframe
- *((uint32_t*) (f->Buffer() + sizeof(eframe))) = frame_size_;
+
+ uint8_t* destBuf = f->Buffer();
+ *reinterpret_cast<uint32_t*>(destBuf) = length;
+ destBuf += sizeof(length);
+
+ switch (nal_type) {
+ case 8:
+ memcpy(destBuf, cannedPps, sizeof(cannedPps));
+ destBuf += sizeof(cannedPps);
+ break;
+ case 7:
+ memcpy(destBuf, cannedSps, sizeof(cannedSps));
+ destBuf += sizeof(cannedSps);
+ break;
+ case 5:
+ memcpy(destBuf, cannedIFramePrefix, sizeof(cannedIFramePrefix));
+ destBuf += sizeof(cannedIFramePrefix);
+ break;
+ case 1:
+ memcpy(destBuf, cannedPFramePrefix, sizeof(cannedPFramePrefix));
+ destBuf += sizeof(cannedPFramePrefix);
+ break;
+ default:
+ MOZ_CRASH();
}
- f->SetEncodedWidth(eframe.width_);
- f->SetEncodedHeight(eframe.height_);
- f->SetTimeStamp(eframe.timestamp_);
+ if (nal_type == 5 || nal_type == 1) {
+ EncodedFrame eframe;
+ eframe.length_ = length;
+ eframe.width_ = inputImage->Width();
+ eframe.height_ = inputImage->Height();
+ eframe.y_ = AveragePlane(inputImage->Buffer(kGMPYPlane),
+ inputImage->AllocatedSize(kGMPYPlane));
+ eframe.u_ = AveragePlane(inputImage->Buffer(kGMPUPlane),
+ inputImage->AllocatedSize(kGMPUPlane));
+ eframe.v_ = AveragePlane(inputImage->Buffer(kGMPVPlane),
+ inputImage->AllocatedSize(kGMPVPlane));
+
+ eframe.timestamp_ = inputImage->Timestamp();
+ memcpy(destBuf, &eframe, sizeof(eframe));
+ destBuf += sizeof(eframe);
+ }
+
+ f->SetEncodedWidth(inputImage->Width());
+ f->SetEncodedHeight(inputImage->Height());
+ f->SetTimeStamp(inputImage->Timestamp());
f->SetFrameType(frame_type);
f->SetCompleteFrame(true);
f->SetBufferType(GMP_BufferLength32);
GMPLOG (GL_DEBUG, "Encoding complete. type= "
<< f->FrameType()
<< " NAL_type="
- << (int) eframe.h264_compat_
+ << (int) nal_type
<< " length="
<< f->Size()
<< " timestamp="
<< f->TimeStamp()
<< " width/height="
- << eframe.width_
- << "x" << eframe.height_);
+ << inputImage->Width()
+ << "x" << inputImage->Height());
// Return the encoded frame.
GMPCodecSpecificInfo info;
mozilla::PodZero(&info);
info.mCodecType = kGMPVideoCodecH264;
info.mBufferType = GMP_BufferLength32;
info.mCodecSpecific.mH264.mSimulcastIdx = 0;
GMPLOG (GL_DEBUG, "Calling callback");
@@ -245,48 +269,41 @@ class FakeVideoEncoder : public GMPVideo
GMPLOG (GL_DEBUG, "Callback called");
}
void Encode (GMPVideoi420Frame* inputImage,
const uint8_t* aCodecSpecificInfo,
uint32_t aCodecSpecificInfoLength,
const GMPVideoFrameType* aFrameTypes,
uint32_t aFrameTypesLength) override {
+ SelfDestruct<GMPVideoi420Frame> ifd (inputImage);
+
GMPLOG (GL_DEBUG,
__FUNCTION__
<< " size="
<< inputImage->Width() << "x" << inputImage->Height());
assert (aFrameTypesLength != 0);
- g_platform_api->runonmainthread(new FakeEncoderTask(this,
- inputImage,
- aFrameTypes[0]));
- }
-
- void Encode_m (GMPVideoi420Frame* inputImage,
- GMPVideoFrameType frame_type) {
- SelfDestruct<GMPVideoi420Frame> ifd (inputImage);
-
- if (frame_type == kGMPKeyFrame) {
+ if (aFrameTypes[0] == kGMPKeyFrame) {
if (!inputImage)
return;
}
if (!inputImage) {
GMPLOG (GL_ERROR, "no input image");
return;
}
- if (frame_type == kGMPKeyFrame ||
+ if ((aFrameTypes[0] == kGMPKeyFrame) ||
frames_encoded_++ % 10 == 0) { // periodically send iframes anyways
SendFrame(inputImage, kGMPKeyFrame, 7); // 7 = SPS, 8 = PPS, 5 = IFrame/IDR slice, 1=PFrame/slice
SendFrame(inputImage, kGMPKeyFrame, 8);
SendFrame(inputImage, kGMPKeyFrame, 5);
} else {
- SendFrame(inputImage, frame_type, 1);
+ SendFrame(inputImage, aFrameTypes[0], 1);
}
}
void SetChannelParameters (uint32_t aPacketLoss, uint32_t aRTT) override {
}
void SetRates (uint32_t aNewBitRate, uint32_t aFrameRate) override {
}
@@ -310,36 +327,16 @@ class FakeVideoEncoder : public GMPVideo
}
GMPVideoHost* host_;
GMPVideoEncoderCallback* callback_;
uint32_t frame_size_;
uint32_t frames_encoded_;
};
-void FakeEncoderTask::Run() {
- encoder_->Encode_m(frame_, type_);
- frame_ = nullptr; // Encode_m() destroys the frame
-}
-
-class FakeDecoderTask : public GMPTask {
- public:
- FakeDecoderTask(FakeVideoDecoder* decoder,
- GMPVideoEncodedFrame* frame,
- int64_t time)
- : decoder_(decoder), frame_(frame), time_(time) {}
-
- void Run() override;
- void Destroy() override { delete this; }
-
- FakeVideoDecoder* decoder_;
- GMPVideoEncodedFrame* frame_;
- int64_t time_;
-};
-
class FakeVideoDecoder : public GMPVideoDecoder {
public:
explicit FakeVideoDecoder (GMPVideoHost* hostAPI) :
host_ (hostAPI),
callback_ (nullptr) {}
~FakeVideoDecoder() override = default;
@@ -357,120 +354,125 @@ class FakeVideoDecoder : public GMPVideo
callback_ = callback;
}
void Decode (GMPVideoEncodedFrame* inputFrame,
bool missingFrames,
const uint8_t* aCodecSpecificInfo,
uint32_t aCodecSpecificInfoLength,
int64_t renderTimeMs = -1) override {
+ // Attach a self-destructor so that the input frame is destroyed on return.
+ SelfDestruct<GMPVideoEncodedFrame> ifd (inputFrame);
+
GMPLOG (GL_DEBUG, __FUNCTION__
<< "Decoding frame size=" << inputFrame->Size()
<< " timestamp=" << inputFrame->TimeStamp());
- g_platform_api->runonmainthread(new FakeDecoderTask(this, inputFrame, renderTimeMs));
+
+ const uint8_t* buf = inputFrame->Buffer();
+
+ if (!buf) {
+ MOZ_CRASH("Buffer is null");
+ }
+
+ if (inputFrame->Size() < sizeof(uint32_t)) {
+ MOZ_CRASH("Not enough buffer for length field");
+ }
+
+ const uint8_t* end = buf + inputFrame->Size();
+
+ size_t size = *reinterpret_cast<const uint32_t*>(buf);
+ if (size != inputFrame->Size()) {
+ MOZ_CRASH("Inconsistent length");
+ }
+ buf += sizeof(uint32_t);
+
+ if (buf >= end) {
+ MOZ_CRASH("Not enough buffer for nal type");
+ }
+
+ uint8_t nal_type = buf[0] & 0x1f;
+
+ if (nal_type == 5 || nal_type == 1) {
+ buf += (nal_type == 5) ? sizeof(cannedPFramePrefix) : sizeof(cannedIFramePrefix);
+ if (buf > end) {
+ MOZ_CRASH("Not enough buffer for frame prefix");
+ }
+
+ if (buf + sizeof(EncodedFrame) > end) {
+ MOZ_CRASH("Not enough buffer for EncodedFrame");
+ }
+
+ const EncodedFrame *eframe = reinterpret_cast<const EncodedFrame*>(buf);
+ int width = eframe->width_;
+ int height = eframe->height_;
+ int ystride = eframe->width_;
+ int uvstride = eframe->width_/2;
+
+ GMPLOG (GL_DEBUG, "Video frame ready for display "
+ << width
+ << "x"
+ << height
+ << " timestamp="
+ << inputFrame->TimeStamp()
+ << " y/u/v=" << (int) eframe->y_ << ":" << (int) eframe->u_ << ":" << (int) eframe->v_);
+
+ GMPVideoFrame* ftmp = nullptr;
+
+ // Translate the image.
+ GMPErr err = host_->CreateFrame (kGMPI420VideoFrame, &ftmp);
+ if (err != GMPNoErr) {
+ GMPLOG (GL_ERROR, "Couldn't allocate empty I420 frame");
+ return;
+ }
+
+ GMPVideoi420Frame* frame = static_cast<GMPVideoi420Frame*> (ftmp);
+ err = frame->CreateEmptyFrame (
+ width, height,
+ ystride, uvstride, uvstride);
+ if (err != GMPNoErr) {
+ GMPLOG (GL_ERROR, "Couldn't make decoded frame");
+ return;
+ }
+
+ memset(frame->Buffer(kGMPYPlane),
+ eframe->y_,
+ frame->AllocatedSize(kGMPYPlane));
+ memset(frame->Buffer(kGMPUPlane),
+ eframe->u_,
+ frame->AllocatedSize(kGMPUPlane));
+ memset(frame->Buffer(kGMPVPlane),
+ eframe->v_,
+ frame->AllocatedSize(kGMPVPlane));
+
+ GMPLOG (GL_DEBUG, "Allocated size = "
+ << frame->AllocatedSize (kGMPYPlane));
+ frame->SetTimestamp (inputFrame->TimeStamp());
+ frame->SetDuration (inputFrame->Duration());
+ callback_->Decoded (frame);
+ }
}
void Reset() override {
}
void Drain() override {
}
void DecodingComplete() override {
delete this;
}
- // Return the decoded data back to the parent.
- void Decode_m (GMPVideoEncodedFrame* inputFrame,
- int64_t renderTimeMs) {
- // Attach a self-destructor so that the input frame is destroyed on return.
- SelfDestruct<GMPVideoEncodedFrame> ifd (inputFrame);
-
- EncodedFrame *eframe;
- eframe = reinterpret_cast<EncodedFrame*>(inputFrame->Buffer());
- GMPLOG(GL_DEBUG,"magic=" << eframe->magic_ << " h264_compat=" << (int) eframe->h264_compat_
- << " width=" << eframe->width_ << " height=" << eframe->height_
- << " timestamp=" << inputFrame->TimeStamp()
- << " y/u/v=" << (int) eframe->y_ << ":" << (int) eframe->u_ << ":" << (int) eframe->v_);
- if (inputFrame->Size() != (sizeof(*eframe))) {
- GMPLOG (GL_ERROR, "Couldn't decode frame. Size=" << inputFrame->Size());
- return;
- }
-
- if (eframe->magic_ != ENCODED_FRAME_MAGIC) {
- GMPLOG (GL_ERROR, "Couldn't decode frame. Magic=" << eframe->magic_);
- return;
- }
- if (eframe->h264_compat_ != 5 && eframe->h264_compat_ != 1) {
- // only return video for iframes or pframes
- GMPLOG (GL_DEBUG, "Not a video frame: NAL type " << (int) eframe->h264_compat_);
- // Decode it anyways
- }
-
- int width = eframe->width_;
- int height = eframe->height_;
- int ystride = eframe->width_;
- int uvstride = eframe->width_/2;
-
- GMPLOG (GL_DEBUG, "Video frame ready for display "
- << width
- << "x"
- << height
- << " timestamp="
- << inputFrame->TimeStamp());
-
- GMPVideoFrame* ftmp = nullptr;
-
- // Translate the image.
- GMPErr err = host_->CreateFrame (kGMPI420VideoFrame, &ftmp);
- if (err != GMPNoErr) {
- GMPLOG (GL_ERROR, "Couldn't allocate empty I420 frame");
- return;
- }
-
- GMPVideoi420Frame* frame = static_cast<GMPVideoi420Frame*> (ftmp);
- err = frame->CreateEmptyFrame (
- width, height,
- ystride, uvstride, uvstride);
- if (err != GMPNoErr) {
- GMPLOG (GL_ERROR, "Couldn't make decoded frame");
- return;
- }
-
- memset(frame->Buffer(kGMPYPlane),
- eframe->y_,
- frame->AllocatedSize(kGMPYPlane));
- memset(frame->Buffer(kGMPUPlane),
- eframe->u_,
- frame->AllocatedSize(kGMPUPlane));
- memset(frame->Buffer(kGMPVPlane),
- eframe->v_,
- frame->AllocatedSize(kGMPVPlane));
-
- GMPLOG (GL_DEBUG, "Allocated size = "
- << frame->AllocatedSize (kGMPYPlane));
- frame->SetTimestamp (inputFrame->TimeStamp());
- frame->SetDuration (inputFrame->Duration());
- callback_->Decoded (frame);
- }
-
GMPVideoHost* host_;
GMPVideoDecoderCallback* callback_;
};
-void FakeDecoderTask::Run() {
- decoder_->Decode_m(frame_, time_);
- frame_ = nullptr; // Decode_m() destroys the frame
-}
-
extern "C" {
PUBLIC_FUNC GMPErr
GMPInit (GMPPlatformAPI* aPlatformAPI) {
- g_platform_api = aPlatformAPI;
return GMPNoErr;
}
PUBLIC_FUNC GMPErr
GMPGetAPI (const char* aApiName, void* aHostAPI, void** aPluginApi) {
if (!strcmp (aApiName, GMP_API_VIDEO_DECODER)) {
*aPluginApi = new FakeVideoDecoder (static_cast<GMPVideoHost*> (aHostAPI));
return GMPNoErr;
@@ -479,12 +481,11 @@ extern "C" {
*aPluginApi = new FakeVideoEncoder (static_cast<GMPVideoHost*> (aHostAPI));
return GMPNoErr;
}
return GMPGenericErr;
}
PUBLIC_FUNC void
GMPShutdown (void) {
- g_platform_api = nullptr;
}
} // extern "C"