/* * Copyright (C) 2024 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <arpa/inet.h> #include <sys/time.h> #include <utility> #include "videodec_api11_sample.h" #include "native_avcapability.h" using namespace OHOS; using namespace OHOS::Media; using namespace std; namespace { const string MIME_TYPE = "video/avc"; constexpr int64_t NANOS_IN_SECOND = 1000000000L; constexpr int64_t NANOS_IN_MICRO = 1000L; constexpr uint32_t START_CODE_SIZE = 4; constexpr uint8_t SPS = 7; constexpr uint8_t PPS = 8; constexpr int32_t EIGHT = 8; constexpr int32_t SIXTEEN = 16; constexpr int32_t TWENTY_FOUR = 24; constexpr uint8_t H264_NALU_TYPE = 0x1f; constexpr uint8_t START_CODE[START_CODE_SIZE] = {0, 0, 0, 1}; VDecApi11FuzzSample *g_decSample = nullptr; void clearIntqueue(std::queue<uint32_t> &q) { std::queue<uint32_t> empty; swap(empty, q); } void clearAvBufferQueue(std::queue<OH_AVBuffer *> &q) { std::queue<OH_AVBuffer *> empty; swap(empty, q); } } // namespace class TestConsumerListener : public IBufferConsumerListener { public: TestConsumerListener(sptr<Surface> cs) : cs(cs) {}; ~TestConsumerListener() {} void OnBufferAvailable() override { sptr<SurfaceBuffer> buffer; int32_t flushFence; cs->AcquireBuffer(buffer, flushFence, timestamp, damage); cs->ReleaseBuffer(buffer, -1); } private: int64_t timestamp = 0; Rect damage = {}; sptr<Surface> cs {nullptr}; }; VDecApi11FuzzSample::~VDecApi11FuzzSample() { Release(); } void VdecError(OH_AVCodec *codec, int32_t errorCode, void *userData) { cout << "Error errorCode=" << errorCode << endl; } void VdecFormatChanged(OH_AVCodec *codec, OH_AVFormat *format, void *userData) { cout << "Format Changed" << endl; int32_t currentWidth = 0; int32_t currentHeight = 0; OH_AVFormat_GetIntValue(format, OH_MD_KEY_WIDTH, ¤tWidth); OH_AVFormat_GetIntValue(format, OH_MD_KEY_HEIGHT, ¤tHeight); g_decSample->defaultWidth = currentWidth; g_decSample->defaultHeight = currentHeight; } void VdecInputDataReady(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData) { VDecSignal *signal = static_cast<VDecSignal *>(userData); if (signal == nullptr) { return; } unique_lock<mutex> lock(signal->inMutex_); signal->inIdxQueue_.push(index); signal->inBufferQueue_.push(buffer); signal->inCond_.notify_all(); } void VdecOutputDataReady(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData) { int32_t ret = 0; if (g_decSample->isSurfMode) { ret = OH_VideoDecoder_RenderOutputBuffer(codec, index); } else { ret = OH_VideoDecoder_FreeOutputBuffer(codec, index); } if (ret != AV_ERR_OK) { g_decSample->Flush(); g_decSample->Start(); } } int64_t VDecApi11FuzzSample::GetSystemTimeUs() { struct timespec now; (void)clock_gettime(CLOCK_BOOTTIME, &now); int64_t nanoTime = static_cast<int64_t>(now.tv_sec) * NANOS_IN_SECOND + now.tv_nsec; return nanoTime / NANOS_IN_MICRO; } int32_t VDecApi11FuzzSample::ConfigureVideoDecoder() { OH_AVFormat *format = OH_AVFormat_Create(); if (format == nullptr) { cout << "Fatal: Failed to create format" << endl; return AV_ERR_UNKNOWN; } (void)OH_AVFormat_SetIntValue(format, OH_MD_KEY_WIDTH, defaultWidth); (void)OH_AVFormat_SetIntValue(format, OH_MD_KEY_HEIGHT, defaultHeight); (void)OH_AVFormat_SetDoubleValue(format, OH_MD_KEY_FRAME_RATE, defaultFrameRate); (void)OH_AVFormat_SetIntValue(format, OH_MD_KEY_ROTATION, 0); (void)OH_AVFormat_SetIntValue(format, OH_MD_KEY_PIXEL_FORMAT, AV_PIXEL_FORMAT_NV12); int ret = OH_VideoDecoder_Configure(vdec_, format); OH_AVFormat_Destroy(format); if (isSurfMode) { cs = Surface::CreateSurfaceAsConsumer(); sptr<IBufferConsumerListener> listener = new TestConsumerListener(cs); cs->RegisterConsumerListener(listener); auto p = cs->GetProducer(); ps = Surface::CreateSurfaceAsProducer(p); nativeWindow = CreateNativeWindowFromSurface(&ps); OH_VideoDecoder_SetSurface(vdec_, nativeWindow); } return ret; } int32_t VDecApi11FuzzSample::SetVideoDecoderCallback() { signal_ = new VDecSignal(); if (signal_ == nullptr) { cout << "Failed to new VDecSignal" << endl; return AV_ERR_UNKNOWN; } cb_.onError = VdecError; cb_.onStreamChanged = VdecFormatChanged; cb_.onNeedInputBuffer = VdecInputDataReady; cb_.onNewOutputBuffer = VdecOutputDataReady; OH_VideoDecoder_RegisterCallback(vdec_, cb_, static_cast<void *>(signal_)); return OH_VideoDecoder_RegisterCallback(vdec_, cb_, static_cast<void *>(signal_)); } int32_t VDecApi11FuzzSample::CreateVideoDecoder() { OH_AVCapability *cap = OH_AVCodec_GetCapabilityByCategory(OH_AVCODEC_MIMETYPE_VIDEO_AVC, false, HARDWARE); string codecName = OH_AVCapability_GetName(cap); vdec_ = OH_VideoDecoder_CreateByName("aabbcc"); if (vdec_) { OH_VideoDecoder_Destroy(vdec_); vdec_ = nullptr; } OH_AVCodec *tmpDec = OH_VideoDecoder_CreateByMime("aabbcc"); if (tmpDec) { OH_VideoDecoder_Destroy(tmpDec); tmpDec = nullptr; } tmpDec = OH_VideoDecoder_CreateByMime(OH_AVCODEC_MIMETYPE_VIDEO_AVC); if (tmpDec) { OH_VideoDecoder_Destroy(tmpDec); tmpDec = nullptr; } vdec_ = OH_VideoDecoder_CreateByName(codecName.c_str()); g_decSample = this; return vdec_ == nullptr ? AV_ERR_UNKNOWN : AV_ERR_OK; } void VDecApi11FuzzSample::WaitForEOS() { if (inputLoop_ && inputLoop_->joinable()) { inputLoop_->join(); } } OH_AVErrCode VDecApi11FuzzSample::InputFuncFUZZ(const uint8_t *data, size_t size) { uint32_t index; unique_lock<mutex> lock(signal_->inMutex_); if (!isRunning_.load()) { return AV_ERR_NO_MEMORY; } signal_->inCond_.wait(lock, [this]() { if (!isRunning_.load()) { return true; } return signal_->inIdxQueue_.size() > 0; }); if (!isRunning_.load()) { return AV_ERR_NO_MEMORY; } index = signal_->inIdxQueue_.front(); auto buffer = signal_->inBufferQueue_.front(); lock.unlock(); int32_t bufferSize = OH_AVBuffer_GetCapacity(buffer); uint8_t *bufferAddr = OH_AVBuffer_GetAddr(buffer); if (size > bufferSize - START_CODE_SIZE) { cout << "Fatal: memcpy fail" << endl; return AV_ERR_NO_MEMORY; } if (memcpy_s(bufferAddr, bufferSize, START_CODE, START_CODE_SIZE) != EOK) { cout << "Fatal: memcpy fail" << endl; return AV_ERR_NO_MEMORY; } if (memcpy_s(bufferAddr + START_CODE_SIZE, bufferSize - START_CODE_SIZE, data, size) != EOK) { cout << "Fatal: memcpy fail" << endl; return AV_ERR_NO_MEMORY; } OH_AVCodecBufferAttr attr; attr.pts = GetSystemTimeUs(); attr.size = size; attr.offset = 0; attr.flags = AVCODEC_BUFFER_FLAGS_NONE; OH_AVBuffer_SetBufferAttr(buffer, &attr); OH_AVErrCode ret = OH_VideoDecoder_PushInputBuffer(vdec_, index); signal_->inIdxQueue_.pop(); signal_->inBufferQueue_.pop(); return ret; } void VDecApi11FuzzSample::SetEOS(OH_AVBuffer *buffer, uint32_t index) { OH_AVCodecBufferAttr attr; attr.pts = 0; attr.size = 0; attr.offset = 0; attr.flags = AVCODEC_BUFFER_FLAGS_EOS; OH_AVBuffer_SetBufferAttr(buffer, &attr); int32_t res = OH_VideoDecoder_PushInputBuffer(vdec_, index); cout << "OH_VideoDecoder_PushInputData EOS res: " << res << endl; } int32_t VDecApi11FuzzSample::Flush() { unique_lock<mutex> inLock(signal_->inMutex_); clearIntqueue(signal_->inIdxQueue_); clearAvBufferQueue(signal_->inBufferQueue_); signal_->inCond_.notify_all(); inLock.unlock(); unique_lock<mutex> outLock(signal_->outMutex_); clearIntqueue(signal_->outIdxQueue_); signal_->outCond_.notify_all(); isRunning_.store(false); outLock.unlock(); return OH_VideoDecoder_Flush(vdec_); } int32_t VDecApi11FuzzSample::Reset() { isRunning_.store(false); return OH_VideoDecoder_Reset(vdec_); } int32_t VDecApi11FuzzSample::Release() { int ret = 0; if (vdec_ != nullptr) { ret = OH_VideoDecoder_Destroy(vdec_); vdec_ = nullptr; } if (signal_ != nullptr) { clearAvBufferQueue(signal_->inBufferQueue_); delete signal_; signal_ = nullptr; } return ret; } int32_t VDecApi11FuzzSample::Stop() { clearIntqueue(signal_->outIdxQueue_); isRunning_.store(false); return OH_VideoDecoder_Stop(vdec_); } int32_t VDecApi11FuzzSample::Start() { int32_t ret = OH_VideoDecoder_Start(vdec_); if (ret == AV_ERR_OK) { isRunning_.store(true); } return ret; } void VDecApi11FuzzSample::SetParameter(int32_t data) { OH_AVFormat *format = OH_AVFormat_Create(); (void)OH_AVFormat_SetIntValue(format, OH_MD_KEY_WIDTH, data); (void)OH_AVFormat_SetIntValue(format, OH_MD_KEY_HEIGHT, data); OH_VideoDecoder_SetParameter(vdec_, format); OH_AVFormat_Destroy(format); } void VDecApi11FuzzSample::StopInloop() { if (inputLoop_ != nullptr && inputLoop_->joinable()) { unique_lock<mutex> lock(signal_->inMutex_); clearIntqueue(signal_->inIdxQueue_); isRunning_.store(false); signal_->inCond_.notify_all(); lock.unlock(); inputLoop_->join(); inputLoop_.reset(); } } void VDecApi11FuzzSample::ReleaseInFile() { if (inFile_ != nullptr) { if (inFile_->is_open()) { inFile_->close(); } inFile_.reset(); inFile_ = nullptr; } } int32_t VDecApi11FuzzSample::StartVideoDecoder() { isRunning_.store(true); int ret = OH_VideoDecoder_Start(vdec_); if (ret != AV_ERR_OK) { isRunning_.store(false); ReleaseInFile(); Release(); cout << "Failed to start codec" << endl; return ret; } inFile_ = make_unique<ifstream>(); if (inFile_ == nullptr) { isRunning_.store(false); (void)OH_VideoDecoder_Stop(vdec_); return AV_ERR_UNKNOWN; } inFile_->open(inpDir, ios::in | ios::binary); if (!inFile_->is_open()) { cout << "open input file failed" << endl; isRunning_.store(false); (void)OH_VideoDecoder_Stop(vdec_); inFile_->close(); inFile_.reset(); inFile_ = nullptr; return AV_ERR_UNKNOWN; } inputLoop_ = make_unique<thread>(&VDecApi11FuzzSample::InputFuncTest, this); if (inputLoop_ == nullptr) { cout << "Failed to create input loop" << endl; isRunning_.store(false); (void)OH_VideoDecoder_Stop(vdec_); ReleaseInFile(); return AV_ERR_UNKNOWN; } return AV_ERR_OK; } void VDecApi11FuzzSample::InputFuncTest() { while (isRunning_.load()) { uint32_t index; unique_lock<mutex> lock(signal_->inMutex_); signal_->inCond_.wait(lock, [this]() { if (!isRunning_.load()) { return true; } return signal_->inIdxQueue_.size() > 0; }); if (!isRunning_.load()) { break; } index = signal_->inIdxQueue_.front(); auto buffer = signal_->inBufferQueue_.front(); signal_->inIdxQueue_.pop(); signal_->inBufferQueue_.pop(); lock.unlock(); if (!inFile_->eof()) { int ret = PushData(index, buffer); if (ret == 1) { break; } } } } int32_t VDecApi11FuzzSample::PushData(uint32_t index, OH_AVBuffer *buffer) { char ch[4] = {}; (void)inFile_->read(ch, START_CODE_SIZE); if (inFile_->eof()) { SetEOS(buffer, index); return 1; } uint32_t bufferSize = static_cast<uint32_t>(((ch[3] & 0xFF)) | ((ch[2] & 0xFF) << EIGHT) | ((ch[1] & 0xFF) << SIXTEEN) | ((ch[0] & 0xFF) << TWENTY_FOUR)); return SendData(bufferSize, index, buffer); } uint32_t VDecApi11FuzzSample::SendData(uint32_t bufferSize, uint32_t index, OH_AVBuffer *buffer) { OH_AVCodecBufferAttr attr; uint8_t *fileBuffer = new uint8_t[bufferSize + START_CODE_SIZE]; if (fileBuffer == nullptr) { delete[] fileBuffer; return 0; } if (memcpy_s(fileBuffer, bufferSize + START_CODE_SIZE, START_CODE, START_CODE_SIZE) != EOK) { cout << "Fatal: memory copy failed" << endl; } (void)inFile_->read((char *)fileBuffer + START_CODE_SIZE, bufferSize); if ((fileBuffer[START_CODE_SIZE] & H264_NALU_TYPE) == SPS || (fileBuffer[START_CODE_SIZE] & H264_NALU_TYPE) == PPS) { attr.flags = AVCODEC_BUFFER_FLAGS_CODEC_DATA; } else { attr.flags = AVCODEC_BUFFER_FLAGS_NONE; } int32_t size = OH_AVBuffer_GetCapacity(buffer); if (size < bufferSize + START_CODE_SIZE) { delete[] fileBuffer; return 0; } uint8_t *avBuffer = OH_AVBuffer_GetAddr(buffer); if (avBuffer == nullptr) { cout << "avBuffer == nullptr" << endl; inFile_->clear(); inFile_->seekg(0, ios::beg); delete[] fileBuffer; return 0; } if (memcpy_s(avBuffer, size, fileBuffer, bufferSize + START_CODE_SIZE) != EOK) { delete[] fileBuffer; return 0; } int64_t startPts = GetSystemTimeUs(); attr.pts = startPts; attr.size = bufferSize + START_CODE_SIZE; attr.offset = 0; OH_AVBuffer_SetBufferAttr(buffer, &attr); if (isRunning_.load()) { OH_VideoDecoder_PushInputBuffer(vdec_, index) == AV_ERR_OK ? (0) : (errCount++); frameCount_ = frameCount_ + 1; } delete[] fileBuffer; return 0; }