#pragma clang diagnostic ignored "-Wdeprecated-declarations" #include "selfdrive/loggerd/omx_encoder.h" #include #include #include #include #include #include #include #include #include #include #include #include "libyuv.h" #include "selfdrive/common/swaglog.h" #include "selfdrive/common/util.h" #include "selfdrive/loggerd/include/msm_media_info.h" // Check the OMX error code and assert if an error occurred. #define OMX_CHECK(_expr) \ do { \ assert(OMX_ErrorNone == _expr); \ } while (0) extern ExitHandler do_exit; // ***** OMX callback functions ***** void OmxEncoder::wait_for_state(OMX_STATETYPE state) { std::unique_lock lk(this->state_lock); while (this->state != state) { this->state_cv.wait(lk); } } static OMX_CALLBACKTYPE omx_callbacks = { .EventHandler = OmxEncoder::event_handler, .EmptyBufferDone = OmxEncoder::empty_buffer_done, .FillBufferDone = OmxEncoder::fill_buffer_done, }; OMX_ERRORTYPE OmxEncoder::event_handler(OMX_HANDLETYPE component, OMX_PTR app_data, OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2, OMX_PTR event_data) { OmxEncoder *e = (OmxEncoder*)app_data; if (event == OMX_EventCmdComplete) { assert(data1 == OMX_CommandStateSet); LOG("set state event 0x%x", data2); { std::unique_lock lk(e->state_lock); e->state = (OMX_STATETYPE)data2; } e->state_cv.notify_all(); } else if (event == OMX_EventError) { LOGE("OMX error 0x%08x", data1); } else { LOGE("OMX unhandled event %d", event); assert(false); } return OMX_ErrorNone; } OMX_ERRORTYPE OmxEncoder::empty_buffer_done(OMX_HANDLETYPE component, OMX_PTR app_data, OMX_BUFFERHEADERTYPE *buffer) { // printf("empty_buffer_done\n"); OmxEncoder *e = (OmxEncoder*)app_data; e->free_in.push(buffer); return OMX_ErrorNone; } OMX_ERRORTYPE OmxEncoder::fill_buffer_done(OMX_HANDLETYPE component, OMX_PTR app_data, OMX_BUFFERHEADERTYPE *buffer) { // printf("fill_buffer_done\n"); OmxEncoder *e = (OmxEncoder*)app_data; e->done_out.push(buffer); return OMX_ErrorNone; } #define PORT_INDEX_IN 0 #define PORT_INDEX_OUT 1 static const char* omx_color_fomat_name(uint32_t format) __attribute__((unused)); static const char* omx_color_fomat_name(uint32_t format) { switch (format) { case OMX_COLOR_FormatUnused: return "OMX_COLOR_FormatUnused"; case OMX_COLOR_FormatMonochrome: return "OMX_COLOR_FormatMonochrome"; case OMX_COLOR_Format8bitRGB332: return "OMX_COLOR_Format8bitRGB332"; case OMX_COLOR_Format12bitRGB444: return "OMX_COLOR_Format12bitRGB444"; case OMX_COLOR_Format16bitARGB4444: return "OMX_COLOR_Format16bitARGB4444"; case OMX_COLOR_Format16bitARGB1555: return "OMX_COLOR_Format16bitARGB1555"; case OMX_COLOR_Format16bitRGB565: return "OMX_COLOR_Format16bitRGB565"; case OMX_COLOR_Format16bitBGR565: return "OMX_COLOR_Format16bitBGR565"; case OMX_COLOR_Format18bitRGB666: return "OMX_COLOR_Format18bitRGB666"; case OMX_COLOR_Format18bitARGB1665: return "OMX_COLOR_Format18bitARGB1665"; case OMX_COLOR_Format19bitARGB1666: return "OMX_COLOR_Format19bitARGB1666"; case OMX_COLOR_Format24bitRGB888: return "OMX_COLOR_Format24bitRGB888"; case OMX_COLOR_Format24bitBGR888: return "OMX_COLOR_Format24bitBGR888"; case OMX_COLOR_Format24bitARGB1887: return "OMX_COLOR_Format24bitARGB1887"; case OMX_COLOR_Format25bitARGB1888: return "OMX_COLOR_Format25bitARGB1888"; case OMX_COLOR_Format32bitBGRA8888: return "OMX_COLOR_Format32bitBGRA8888"; case OMX_COLOR_Format32bitARGB8888: return "OMX_COLOR_Format32bitARGB8888"; case OMX_COLOR_FormatYUV411Planar: return "OMX_COLOR_FormatYUV411Planar"; case OMX_COLOR_FormatYUV411PackedPlanar: return "OMX_COLOR_FormatYUV411PackedPlanar"; case OMX_COLOR_FormatYUV420Planar: return "OMX_COLOR_FormatYUV420Planar"; case OMX_COLOR_FormatYUV420PackedPlanar: return "OMX_COLOR_FormatYUV420PackedPlanar"; case OMX_COLOR_FormatYUV420SemiPlanar: return "OMX_COLOR_FormatYUV420SemiPlanar"; case OMX_COLOR_FormatYUV422Planar: return "OMX_COLOR_FormatYUV422Planar"; case OMX_COLOR_FormatYUV422PackedPlanar: return "OMX_COLOR_FormatYUV422PackedPlanar"; case OMX_COLOR_FormatYUV422SemiPlanar: return "OMX_COLOR_FormatYUV422SemiPlanar"; case OMX_COLOR_FormatYCbYCr: return "OMX_COLOR_FormatYCbYCr"; case OMX_COLOR_FormatYCrYCb: return "OMX_COLOR_FormatYCrYCb"; case OMX_COLOR_FormatCbYCrY: return "OMX_COLOR_FormatCbYCrY"; case OMX_COLOR_FormatCrYCbY: return "OMX_COLOR_FormatCrYCbY"; case OMX_COLOR_FormatYUV444Interleaved: return "OMX_COLOR_FormatYUV444Interleaved"; case OMX_COLOR_FormatRawBayer8bit: return "OMX_COLOR_FormatRawBayer8bit"; case OMX_COLOR_FormatRawBayer10bit: return "OMX_COLOR_FormatRawBayer10bit"; case OMX_COLOR_FormatRawBayer8bitcompressed: return "OMX_COLOR_FormatRawBayer8bitcompressed"; case OMX_COLOR_FormatL2: return "OMX_COLOR_FormatL2"; case OMX_COLOR_FormatL4: return "OMX_COLOR_FormatL4"; case OMX_COLOR_FormatL8: return "OMX_COLOR_FormatL8"; case OMX_COLOR_FormatL16: return "OMX_COLOR_FormatL16"; case OMX_COLOR_FormatL24: return "OMX_COLOR_FormatL24"; case OMX_COLOR_FormatL32: return "OMX_COLOR_FormatL32"; case OMX_COLOR_FormatYUV420PackedSemiPlanar: return "OMX_COLOR_FormatYUV420PackedSemiPlanar"; case OMX_COLOR_FormatYUV422PackedSemiPlanar: return "OMX_COLOR_FormatYUV422PackedSemiPlanar"; case OMX_COLOR_Format18BitBGR666: return "OMX_COLOR_Format18BitBGR666"; case OMX_COLOR_Format24BitARGB6666: return "OMX_COLOR_Format24BitARGB6666"; case OMX_COLOR_Format24BitABGR6666: return "OMX_COLOR_Format24BitABGR6666"; case OMX_COLOR_FormatAndroidOpaque: return "OMX_COLOR_FormatAndroidOpaque"; case OMX_TI_COLOR_FormatYUV420PackedSemiPlanar: return "OMX_TI_COLOR_FormatYUV420PackedSemiPlanar"; case OMX_QCOM_COLOR_FormatYVU420SemiPlanar: return "OMX_QCOM_COLOR_FormatYVU420SemiPlanar"; case OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka: return "OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka"; case OMX_SEC_COLOR_FormatNV12Tiled: return "OMX_SEC_COLOR_FormatNV12Tiled"; case OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar32m: return "OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar32m"; // case QOMX_COLOR_FormatYVU420SemiPlanar: return "QOMX_COLOR_FormatYVU420SemiPlanar"; case QOMX_COLOR_FormatYVU420PackedSemiPlanar32m4ka: return "QOMX_COLOR_FormatYVU420PackedSemiPlanar32m4ka"; case QOMX_COLOR_FormatYUV420PackedSemiPlanar16m2ka: return "QOMX_COLOR_FormatYUV420PackedSemiPlanar16m2ka"; // case QOMX_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka: return "QOMX_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka"; // case QOMX_COLOR_FORMATYUV420PackedSemiPlanar32m: return "QOMX_COLOR_FORMATYUV420PackedSemiPlanar32m"; case QOMX_COLOR_FORMATYUV420PackedSemiPlanar32mMultiView: return "QOMX_COLOR_FORMATYUV420PackedSemiPlanar32mMultiView"; case QOMX_COLOR_FORMATYUV420PackedSemiPlanar32mCompressed: return "QOMX_COLOR_FORMATYUV420PackedSemiPlanar32mCompressed"; case QOMX_COLOR_Format32bitRGBA8888: return "QOMX_COLOR_Format32bitRGBA8888"; case QOMX_COLOR_Format32bitRGBA8888Compressed: return "QOMX_COLOR_Format32bitRGBA8888Compressed"; default: return "unkn"; } } // ***** encoder functions ***** OmxEncoder::OmxEncoder(const char* filename, int width, int height, int fps, int bitrate, bool h265, bool downscale) { this->filename = filename; this->width = width; this->height = height; this->fps = fps; this->remuxing = !h265; this->downscale = downscale; if (this->downscale) { this->y_ptr2 = (uint8_t *)malloc(this->width*this->height); this->u_ptr2 = (uint8_t *)malloc(this->width*this->height/4); this->v_ptr2 = (uint8_t *)malloc(this->width*this->height/4); } auto component = (OMX_STRING)(h265 ? "OMX.qcom.video.encoder.hevc" : "OMX.qcom.video.encoder.avc"); int err = OMX_GetHandle(&this->handle, component, this, &omx_callbacks); if (err != OMX_ErrorNone) { LOGE("error getting codec: %x", err); } assert(err == OMX_ErrorNone); // printf("handle: %p\n", this->handle); // setup input port OMX_PARAM_PORTDEFINITIONTYPE in_port = {0}; in_port.nSize = sizeof(in_port); in_port.nPortIndex = (OMX_U32) PORT_INDEX_IN; OMX_CHECK(OMX_GetParameter(this->handle, OMX_IndexParamPortDefinition, (OMX_PTR) &in_port)); in_port.format.video.nFrameWidth = this->width; in_port.format.video.nFrameHeight = this->height; in_port.format.video.nStride = VENUS_Y_STRIDE(COLOR_FMT_NV12, this->width); in_port.format.video.nSliceHeight = this->height; // in_port.nBufferSize = (this->width * this->height * 3) / 2; in_port.nBufferSize = VENUS_BUFFER_SIZE(COLOR_FMT_NV12, this->width, this->height); in_port.format.video.xFramerate = (this->fps * 65536); in_port.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused; // in_port.format.video.eColorFormat = OMX_COLOR_FormatYUV420SemiPlanar; in_port.format.video.eColorFormat = (OMX_COLOR_FORMATTYPE)QOMX_COLOR_FORMATYUV420PackedSemiPlanar32m; OMX_CHECK(OMX_SetParameter(this->handle, OMX_IndexParamPortDefinition, (OMX_PTR) &in_port)); OMX_CHECK(OMX_GetParameter(this->handle, OMX_IndexParamPortDefinition, (OMX_PTR) &in_port)); this->in_buf_headers.resize(in_port.nBufferCountActual); // setup output port OMX_PARAM_PORTDEFINITIONTYPE out_port = {0}; out_port.nSize = sizeof(out_port); out_port.nPortIndex = (OMX_U32) PORT_INDEX_OUT; OMX_CHECK(OMX_GetParameter(this->handle, OMX_IndexParamPortDefinition, (OMX_PTR)&out_port)); out_port.format.video.nFrameWidth = this->width; out_port.format.video.nFrameHeight = this->height; out_port.format.video.xFramerate = 0; out_port.format.video.nBitrate = bitrate; if (h265) { out_port.format.video.eCompressionFormat = OMX_VIDEO_CodingHEVC; } else { out_port.format.video.eCompressionFormat = OMX_VIDEO_CodingAVC; } out_port.format.video.eColorFormat = OMX_COLOR_FormatUnused; OMX_CHECK(OMX_SetParameter(this->handle, OMX_IndexParamPortDefinition, (OMX_PTR) &out_port)); OMX_CHECK(OMX_GetParameter(this->handle, OMX_IndexParamPortDefinition, (OMX_PTR) &out_port)); this->out_buf_headers.resize(out_port.nBufferCountActual); OMX_VIDEO_PARAM_BITRATETYPE bitrate_type = {0}; bitrate_type.nSize = sizeof(bitrate_type); bitrate_type.nPortIndex = (OMX_U32) PORT_INDEX_OUT; OMX_CHECK(OMX_GetParameter(this->handle, OMX_IndexParamVideoBitrate, (OMX_PTR) &bitrate_type)); bitrate_type.eControlRate = OMX_Video_ControlRateVariable; bitrate_type.nTargetBitrate = bitrate; OMX_CHECK(OMX_SetParameter(this->handle, OMX_IndexParamVideoBitrate, (OMX_PTR) &bitrate_type)); if (h265) { // setup HEVC #ifndef QCOM2 OMX_VIDEO_PARAM_HEVCTYPE hevc_type = {0}; OMX_INDEXTYPE index_type = (OMX_INDEXTYPE) OMX_IndexParamVideoHevc; #else OMX_VIDEO_PARAM_PROFILELEVELTYPE hevc_type = {0}; OMX_INDEXTYPE index_type = OMX_IndexParamVideoProfileLevelCurrent; #endif hevc_type.nSize = sizeof(hevc_type); hevc_type.nPortIndex = (OMX_U32) PORT_INDEX_OUT; OMX_CHECK(OMX_GetParameter(this->handle, index_type, (OMX_PTR) &hevc_type)); hevc_type.eProfile = OMX_VIDEO_HEVCProfileMain; hevc_type.eLevel = OMX_VIDEO_HEVCHighTierLevel5; OMX_CHECK(OMX_SetParameter(this->handle, index_type, (OMX_PTR) &hevc_type)); } else { // setup h264 OMX_VIDEO_PARAM_AVCTYPE avc = { 0 }; avc.nSize = sizeof(avc); avc.nPortIndex = (OMX_U32) PORT_INDEX_OUT; OMX_CHECK(OMX_GetParameter(this->handle, OMX_IndexParamVideoAvc, &avc)); avc.nBFrames = 0; avc.nPFrames = 15; avc.eProfile = OMX_VIDEO_AVCProfileHigh; avc.eLevel = OMX_VIDEO_AVCLevel31; avc.nAllowedPictureTypes |= OMX_VIDEO_PictureTypeB; avc.eLoopFilterMode = OMX_VIDEO_AVCLoopFilterEnable; avc.nRefFrames = 1; avc.bUseHadamard = OMX_TRUE; avc.bEntropyCodingCABAC = OMX_TRUE; avc.bWeightedPPrediction = OMX_TRUE; avc.bconstIpred = OMX_TRUE; OMX_CHECK(OMX_SetParameter(this->handle, OMX_IndexParamVideoAvc, &avc)); } // for (int i = 0; ; i++) { // OMX_VIDEO_PARAM_PORTFORMATTYPE video_port_format = {0}; // video_port_format.nSize = sizeof(video_port_format); // video_port_format.nIndex = i; // video_port_format.nPortIndex = PORT_INDEX_IN; // if (OMX_GetParameter(this->handle, OMX_IndexParamVideoPortFormat, &video_port_format) != OMX_ErrorNone) // break; // printf("in %d: compression 0x%x format 0x%x %s\n", i, // video_port_format.eCompressionFormat, video_port_format.eColorFormat, // omx_color_fomat_name(video_port_format.eColorFormat)); // } // for (int i=0; i<32; i++) { // OMX_VIDEO_PARAM_PROFILELEVELTYPE params = {0}; // params.nSize = sizeof(params); // params.nPortIndex = PORT_INDEX_OUT; // params.nProfileIndex = i; // if (OMX_GetParameter(this->handle, OMX_IndexParamVideoProfileLevelQuerySupported, ¶ms) != OMX_ErrorNone) // break; // printf("profile %d level 0x%x\n", params.eProfile, params.eLevel); // } OMX_CHECK(OMX_SendCommand(this->handle, OMX_CommandStateSet, OMX_StateIdle, NULL)); for (auto &buf : this->in_buf_headers) { OMX_CHECK(OMX_AllocateBuffer(this->handle, &buf, PORT_INDEX_IN, this, in_port.nBufferSize)); } for (auto &buf : this->out_buf_headers) { OMX_CHECK(OMX_AllocateBuffer(this->handle, &buf, PORT_INDEX_OUT, this, out_port.nBufferSize)); } wait_for_state(OMX_StateIdle); OMX_CHECK(OMX_SendCommand(this->handle, OMX_CommandStateSet, OMX_StateExecuting, NULL)); wait_for_state(OMX_StateExecuting); // give omx all the output buffers for (auto &buf : this->out_buf_headers) { // printf("fill %p\n", this->out_buf_headers[i]); OMX_CHECK(OMX_FillThisBuffer(this->handle, buf)); } // fill the input free queue for (auto &buf : this->in_buf_headers) { this->free_in.push(buf); } } void OmxEncoder::handle_out_buf(OmxEncoder *e, OMX_BUFFERHEADERTYPE *out_buf) { int err; uint8_t *buf_data = out_buf->pBuffer + out_buf->nOffset; if (out_buf->nFlags & OMX_BUFFERFLAG_CODECCONFIG) { if (e->codec_config_len < out_buf->nFilledLen) { e->codec_config = (uint8_t *)realloc(e->codec_config, out_buf->nFilledLen); } e->codec_config_len = out_buf->nFilledLen; memcpy(e->codec_config, buf_data, out_buf->nFilledLen); #ifdef QCOM2 out_buf->nTimeStamp = 0; #endif } if (e->of) { //printf("write %d flags 0x%x\n", out_buf->nFilledLen, out_buf->nFlags); fwrite(buf_data, out_buf->nFilledLen, 1, e->of); } if (e->remuxing) { if (!e->wrote_codec_config && e->codec_config_len > 0) { // extradata will be freed by av_free() in avcodec_free_context() e->codec_ctx->extradata = (uint8_t*)av_mallocz(e->codec_config_len + AV_INPUT_BUFFER_PADDING_SIZE); e->codec_ctx->extradata_size = e->codec_config_len; memcpy(e->codec_ctx->extradata, e->codec_config, e->codec_config_len); err = avcodec_parameters_from_context(e->out_stream->codecpar, e->codec_ctx); assert(err >= 0); err = avformat_write_header(e->ofmt_ctx, NULL); assert(err >= 0); e->wrote_codec_config = true; } if (out_buf->nTimeStamp > 0) { // input timestamps are in microseconds AVRational in_timebase = {1, 1000000}; AVPacket pkt; av_init_packet(&pkt); pkt.data = buf_data; pkt.size = out_buf->nFilledLen; enum AVRounding rnd = static_cast(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX); pkt.pts = pkt.dts = av_rescale_q_rnd(out_buf->nTimeStamp, in_timebase, e->ofmt_ctx->streams[0]->time_base, rnd); pkt.duration = av_rescale_q(50*1000, in_timebase, e->ofmt_ctx->streams[0]->time_base); if (out_buf->nFlags & OMX_BUFFERFLAG_SYNCFRAME) { pkt.flags |= AV_PKT_FLAG_KEY; } err = av_write_frame(e->ofmt_ctx, &pkt); if (err < 0) { LOGW("ts encoder write issue"); } av_free_packet(&pkt); } } // give omx back the buffer #ifdef QCOM2 if (out_buf->nFlags & OMX_BUFFERFLAG_EOS) { out_buf->nTimeStamp = 0; } #endif OMX_CHECK(OMX_FillThisBuffer(e->handle, out_buf)); } int OmxEncoder::encode_frame(const uint8_t *y_ptr, const uint8_t *u_ptr, const uint8_t *v_ptr, int in_width, int in_height, uint64_t ts) { int err; if (!this->is_open) { return -1; } // this sometimes freezes... put it outside the encoder lock so we can still trigger rotates... // THIS IS A REALLY BAD IDEA, but apparently the race has to happen 30 times to trigger this //pthread_mutex_unlock(&this->lock); OMX_BUFFERHEADERTYPE* in_buf = nullptr; while (!this->free_in.try_pop(in_buf, 20)) { if (do_exit) { return -1; } } //pthread_mutex_lock(&this->lock); int ret = this->counter; uint8_t *in_buf_ptr = in_buf->pBuffer; // printf("in_buf ptr %p\n", in_buf_ptr); uint8_t *in_y_ptr = in_buf_ptr; int in_y_stride = VENUS_Y_STRIDE(COLOR_FMT_NV12, this->width); int in_uv_stride = VENUS_UV_STRIDE(COLOR_FMT_NV12, this->width); // uint8_t *in_uv_ptr = in_buf_ptr + (this->width * this->height); uint8_t *in_uv_ptr = in_buf_ptr + (in_y_stride * VENUS_Y_SCANLINES(COLOR_FMT_NV12, this->height)); if (this->downscale) { I420Scale(y_ptr, in_width, u_ptr, in_width/2, v_ptr, in_width/2, in_width, in_height, this->y_ptr2, this->width, this->u_ptr2, this->width/2, this->v_ptr2, this->width/2, this->width, this->height, libyuv::kFilterNone); y_ptr = this->y_ptr2; u_ptr = this->u_ptr2; v_ptr = this->v_ptr2; } err = libyuv::I420ToNV12(y_ptr, this->width, u_ptr, this->width/2, v_ptr, this->width/2, in_y_ptr, in_y_stride, in_uv_ptr, in_uv_stride, this->width, this->height); assert(err == 0); // in_buf->nFilledLen = (this->width*this->height) + (this->width*this->height/2); in_buf->nFilledLen = VENUS_BUFFER_SIZE(COLOR_FMT_NV12, this->width, this->height); in_buf->nFlags = OMX_BUFFERFLAG_ENDOFFRAME; in_buf->nOffset = 0; in_buf->nTimeStamp = ts/1000LL; // OMX_TICKS, in microseconds this->last_t = in_buf->nTimeStamp; OMX_CHECK(OMX_EmptyThisBuffer(this->handle, in_buf)); // pump output while (true) { OMX_BUFFERHEADERTYPE *out_buf; if (!this->done_out.try_pop(out_buf)) { break; } handle_out_buf(this, out_buf); } this->dirty = true; this->counter++; return ret; } void OmxEncoder::encoder_open(const char* path) { int err; snprintf(this->vid_path, sizeof(this->vid_path), "%s/%s", path, this->filename); LOGD("encoder_open %s remuxing:%d", this->vid_path, this->remuxing); if (this->remuxing) { avformat_alloc_output_context2(&this->ofmt_ctx, NULL, NULL, this->vid_path); assert(this->ofmt_ctx); this->out_stream = avformat_new_stream(this->ofmt_ctx, NULL); assert(this->out_stream); // set codec correctly av_register_all(); AVCodec *codec = NULL; codec = avcodec_find_encoder(AV_CODEC_ID_H264); assert(codec); this->codec_ctx = avcodec_alloc_context3(codec); assert(this->codec_ctx); this->codec_ctx->width = this->width; this->codec_ctx->height = this->height; this->codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P; this->codec_ctx->time_base = (AVRational){ 1, this->fps }; err = avio_open(&this->ofmt_ctx->pb, this->vid_path, AVIO_FLAG_WRITE); assert(err >= 0); this->wrote_codec_config = false; } else { this->of = fopen(this->vid_path, "wb"); assert(this->of); #ifndef QCOM2 if (this->codec_config_len > 0) { fwrite(this->codec_config, this->codec_config_len, 1, this->of); } #endif } // create camera lock file snprintf(this->lock_path, sizeof(this->lock_path), "%s/%s.lock", path, this->filename); int lock_fd = open(this->lock_path, O_RDWR | O_CREAT, 0777); assert(lock_fd >= 0); close(lock_fd); this->is_open = true; this->counter = 0; } void OmxEncoder::encoder_close() { if (this->is_open) { if (this->dirty) { // drain output only if there could be frames in the encoder OMX_BUFFERHEADERTYPE* in_buf = this->free_in.pop(); in_buf->nFilledLen = 0; in_buf->nOffset = 0; in_buf->nFlags = OMX_BUFFERFLAG_EOS; in_buf->nTimeStamp = this->last_t + 1000000LL/this->fps; OMX_CHECK(OMX_EmptyThisBuffer(this->handle, in_buf)); while (true) { OMX_BUFFERHEADERTYPE *out_buf = this->done_out.pop(); handle_out_buf(this, out_buf); if (out_buf->nFlags & OMX_BUFFERFLAG_EOS) { break; } } this->dirty = false; } if (this->remuxing) { av_write_trailer(this->ofmt_ctx); avcodec_free_context(&this->codec_ctx); avio_closep(&this->ofmt_ctx->pb); avformat_free_context(this->ofmt_ctx); } else { fclose(this->of); this->of = nullptr; } unlink(this->lock_path); } this->is_open = false; } OmxEncoder::~OmxEncoder() { assert(!this->is_open); OMX_CHECK(OMX_SendCommand(this->handle, OMX_CommandStateSet, OMX_StateIdle, NULL)); wait_for_state(OMX_StateIdle); OMX_CHECK(OMX_SendCommand(this->handle, OMX_CommandStateSet, OMX_StateLoaded, NULL)); for (auto &buf : this->in_buf_headers) { OMX_CHECK(OMX_FreeBuffer(this->handle, PORT_INDEX_IN, buf)); } for (auto &buf : this->out_buf_headers) { OMX_CHECK(OMX_FreeBuffer(this->handle, PORT_INDEX_OUT, buf)); } wait_for_state(OMX_StateLoaded); OMX_CHECK(OMX_FreeHandle(this->handle)); OMX_BUFFERHEADERTYPE *out_buf; while (this->free_in.try_pop(out_buf)); while (this->done_out.try_pop(out_buf)); if (this->codec_config) { free(this->codec_config); } if (this->downscale) { free(this->y_ptr2); free(this->u_ptr2); free(this->v_ptr2); } }