int32_t V4l2Decoder::ioctl(int command, void* arg) { int32_t ret = 0; int port = -1; DEBUG("fd: %d, ioctl command: %s", m_fd[0], IoctlCommandString(command)); switch (command) { case VIDIOC_QBUF: { #ifdef ANDROID struct v4l2_buffer *qbuf = static_cast<struct v4l2_buffer*>(arg); static uint32_t bufferCount = 0; if(qbuf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && m_streamOn[OUTPUT] == false) { ASSERT(qbuf->memory == V4L2_MEMORY_ANDROID_BUFFER_HANDLE); m_bufferHandle.push_back((buffer_handle_t)(qbuf->m.userptr)); bufferCount++; if (bufferCount == m_reqBuffCnt) mapVideoFrames(m_videoWidth, m_videoHeight); } #endif } // no break; case VIDIOC_STREAMON: case VIDIOC_STREAMOFF: case VIDIOC_DQBUF: case VIDIOC_QUERYCAP: ret = V4l2CodecBase::ioctl(command, arg); break; case VIDIOC_REQBUFS: { ret = V4l2CodecBase::ioctl(command, arg); ASSERT(ret == 0); int port = -1; struct v4l2_requestbuffers *reqbufs = static_cast<struct v4l2_requestbuffers *>(arg); GET_PORT_INDEX(port, reqbufs->type, ret); if (port == OUTPUT) { #if ANDROID if (reqbufs->count) m_reqBuffCnt = reqbufs->count; else m_videoFrames.clear(); #else if (!reqbufs->count) { m_eglVaapiImages.clear(); } else { const VideoFormatInfo* outFormat = m_decoder->getFormatInfo(); ASSERT(outFormat && outFormat->width && outFormat->height); ASSERT(m_eglVaapiImages.empty()); for (uint32_t i = 0; i < reqbufs->count; i++) { SharedPtr<EglVaapiImage> image( new EglVaapiImage(m_decoder->getDisplayID(), outFormat->width, outFormat->height)); if (!image->init()) { ERROR("Create egl vaapi image failed"); ret = -1; break; } m_eglVaapiImages.push_back(image); } } #endif } break; } case VIDIOC_QUERYBUF: { struct v4l2_buffer *buffer = static_cast<struct v4l2_buffer*>(arg); GET_PORT_INDEX(port, buffer->type, ret); ASSERT(buffer->memory == m_memoryMode[port]); ASSERT(buffer->index < m_maxBufferCount[port]); ASSERT(buffer->length == m_bufferPlaneCount[port]); ASSERT(m_maxBufferSize[port] > 0); if (port == INPUT) { ASSERT(buffer->memory == V4L2_MEMORY_MMAP); buffer->m.planes[0].length = m_maxBufferSize[INPUT]; buffer->m.planes[0].m.mem_offset = m_maxBufferSize[INPUT] * buffer->index; } else if (port == OUTPUT) { ASSERT(m_maxBufferSize[INPUT] && m_maxBufferCount[INPUT]); // plus input buffer space size, it will be minused in mmap buffer->m.planes[0].m.mem_offset = m_maxBufferSize[OUTPUT] * buffer->index; buffer->m.planes[0].m.mem_offset += m_maxBufferSize[INPUT] * m_maxBufferCount[INPUT]; buffer->m.planes[0].length = m_videoWidth * m_videoHeight; buffer->m.planes[1].m.mem_offset = buffer->m.planes[0].m.mem_offset + buffer->m.planes[0].length; buffer->m.planes[1].length = ((m_videoWidth+1)/2*2) * ((m_videoHeight+1)/2); } } break; case VIDIOC_S_FMT: { struct v4l2_format *format = static_cast<struct v4l2_format *>(arg); ASSERT(!m_streamOn[INPUT] && !m_streamOn[OUTPUT]); if (format->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { // ::Initialize uint32_t size; memcpy(&size, format->fmt.raw_data, sizeof(uint32_t)); if(size <= (sizeof(format->fmt.raw_data)-sizeof(uint32_t))) { uint8_t *ptr = format->fmt.raw_data; ptr += sizeof(uint32_t); m_codecData.assign(ptr, ptr + size); } else { ret = -1; ERROR("unvalid codec size"); } //ASSERT(format->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_NV12M); } else if (format->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { // ::CreateInputBuffers ASSERT(format->fmt.pix_mp.num_planes == 1); ASSERT(format->fmt.pix_mp.plane_fmt[0].sizeimage); m_codecData.clear(); m_decoder.reset( createVideoDecoder(mimeFromV4l2PixelFormat(format->fmt.pix_mp.pixelformat)), releaseVideoDecoder); ASSERT(m_decoder); if (!m_decoder) { ret = -1; } m_maxBufferSize[INPUT] = format->fmt.pix_mp.plane_fmt[0].sizeimage; } else { ret = -1; ERROR("unknow type: %d of setting format VIDIOC_S_FMT", format->type); } } break; case VIDIOC_SUBSCRIBE_EVENT: { // ::Initialize struct v4l2_event_subscription *sub = static_cast<struct v4l2_event_subscription*>(arg); ASSERT(sub->type == V4L2_EVENT_RESOLUTION_CHANGE); // resolution change event is must, we always do so } break; case VIDIOC_DQEVENT: { // ::DequeueEvents struct v4l2_event *ev = static_cast<struct v4l2_event*>(arg); // notify resolution change if (hasCodecEvent()) { ev->type = V4L2_EVENT_RESOLUTION_CHANGE; clearCodecEvent(); } else ret = -1; } break; case VIDIOC_G_FMT: { // ::GetFormatInfo struct v4l2_format* format = static_cast<struct v4l2_format*>(arg); ASSERT(format->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); ASSERT(m_decoder); const VideoFormatInfo* outFormat = m_decoder->getFormatInfo(); if (format && outFormat && outFormat->width && outFormat->height) { format->fmt.pix_mp.num_planes = m_bufferPlaneCount[OUTPUT]; format->fmt.pix_mp.width = outFormat->width; format->fmt.pix_mp.height = outFormat->height; // XXX assumed output format and pitch #ifdef ANDROID format->fmt.pix_mp.pixelformat = HAL_PIXEL_FORMAT_NV12_Y_TILED_INTEL; #else format->fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12M; #endif format->fmt.pix_mp.plane_fmt[0].bytesperline = outFormat->width; format->fmt.pix_mp.plane_fmt[1].bytesperline = outFormat->width % 2 ? outFormat->width+1 : outFormat->width; m_videoWidth = outFormat->width; m_videoHeight = outFormat->height; m_maxBufferSize[OUTPUT] = m_videoWidth * m_videoHeight + ((m_videoWidth +1)/2*2) * ((m_videoHeight+1)/2); } else { ret = -1; // chromeos accepts EINVAL as not enough input data yet, will try it again. errno = EINVAL; } } break; case VIDIOC_G_CTRL: { // ::CreateOutputBuffers struct v4l2_control* ctrl = static_cast<struct v4l2_control*>(arg); ASSERT(ctrl->id == V4L2_CID_MIN_BUFFERS_FOR_CAPTURE); ASSERT(m_decoder); // VideoFormatInfo* outFormat = m_decoder->getFormatInfo(); ctrl->value = 0; // no need report dpb size, we hold all buffers in decoder. } break; case VIDIOC_ENUM_FMT: { struct v4l2_fmtdesc *fmtdesc = static_cast<struct v4l2_fmtdesc *>(arg); if ((fmtdesc->index == 0) && (fmtdesc->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)) { fmtdesc->pixelformat = V4L2_PIX_FMT_NV12M; } else if ((fmtdesc->index == 0) && (fmtdesc->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)) { fmtdesc->pixelformat = V4L2_PIX_FMT_VP8; } else if ((fmtdesc->index == 1) && (fmtdesc->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)) { fmtdesc->pixelformat = V4L2_PIX_FMT_VP9; } else { ret = -1; } } break; case VIDIOC_G_CROP: { struct v4l2_crop* crop= static_cast<struct v4l2_crop *>(arg); ASSERT(crop->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); ASSERT(m_decoder); const VideoFormatInfo* outFormat = m_decoder->getFormatInfo(); if (outFormat && outFormat->width && outFormat->height) { crop->c.left = 0; crop->c.top = 0; crop->c.width = outFormat->width; crop->c.height = outFormat->height; } else { ret = -1; } } break; default: ret = -1; ERROR("unknown ioctrl command: %d", command); break; } if (ret == -1 && errno != EAGAIN) { // ERROR("ioctl failed"); WARNING("ioctl command: %s failed", IoctlCommandString(command)); } return ret; }
int32_t V4l2Encoder::ioctl(int command, void* arg) { Encode_Status encodeStatus = ENCODE_SUCCESS; int32_t ret = 0; DEBUG("fd: %d, ioctl command: %s", m_fd[0], IoctlCommandString(command)); switch (command) { case VIDIOC_QUERYCAP: case VIDIOC_STREAMON: case VIDIOC_STREAMOFF: case VIDIOC_REQBUFS: case VIDIOC_QBUF: case VIDIOC_DQBUF: ret = V4l2CodecBase::ioctl(command, arg); break; case VIDIOC_QUERYBUF: { struct v4l2_buffer *buffer = static_cast<struct v4l2_buffer*>(arg); ASSERT (buffer->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); ASSERT(buffer->memory == V4L2_MEMORY_MMAP); ASSERT(buffer->index>=0 && buffer->index<m_maxBufferCount[OUTPUT]); ASSERT(buffer->length == m_bufferPlaneCount[OUTPUT]); ASSERT(m_maxOutputBufferSize > 0); buffer->m.planes[0].length = m_maxOutputBufferSize; buffer->m.planes[0].m.mem_offset = m_maxOutputBufferSize * buffer->index; } break; case VIDIOC_S_EXT_CTRLS: { int i; struct v4l2_ext_controls *control = static_cast<struct v4l2_ext_controls *>(arg); DEBUG("V4L2_CTRL_CLASS_MPEG: %d, control->ctrl_class: %d", V4L2_CTRL_CLASS_MPEG, control->ctrl_class); if (control->ctrl_class == V4L2_CTRL_CLASS_MPEG) { AutoLock locker(m_videoParamsLock); struct v4l2_ext_control *ctrls = control->controls; DEBUG("control->count: %d", control->count); for (i=0; i<control->count; i++) { DEBUG("VIDIOC_S_EXT_CTRLS:V4L2_CTRL_CLASS_MPEG:%d", ctrls->id); switch (ctrls->id) { case V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE: // ::EncodeTask ASSERT(ctrls->value == V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_I_FRAME); m_forceKeyFrame = true; break; case V4L2_CID_MPEG_VIDEO_BITRATE: { // ::RequestEncodingParametersChangeTask m_videoParams.rcParams.bitRate = ctrls->value; m_videoParamsChanged = true; } break; case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE: INFO("enable bitrate control"); m_videoParams.rcMode = RATE_CONTROL_CBR; m_videoParamsChanged = true; break; case V4L2_CID_MPEG_VIDEO_HEADER_MODE: // Separate stream header so we can cache it and insert into the stream. if (ctrls->value == V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE) m_separatedStreamHeader = true; INFO("use separated stream header: %d", m_separatedStreamHeader); break; case V4L2_CID_MPEG_VIDEO_B_FRAMES: case V4L2_CID_MPEG_MFC51_VIDEO_RC_REACTION_COEFF: case V4L2_CID_MPEG_MFC51_VIDEO_RC_FIXED_TARGET_BIT: case V4L2_CID_MPEG_VIDEO_H264_MAX_QP: case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE: case V4L2_CID_MPEG_VIDEO_H264_LEVEL: case V4L2_CID_MPEG_VIDEO_GOP_SIZE: default: break; } ctrls++; } } UpdateVideoParameters(); } break; case VIDIOC_S_PARM: { struct v4l2_streamparm *parms = static_cast<struct v4l2_streamparm *>(arg); if (parms->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { AutoLock locker(m_videoParamsLock); // ::RequestEncodingParametersChangeTask m_videoParams.frameRate.frameRateNum = parms->parm.output.timeperframe.denominator; m_videoParams.frameRate.frameRateDenom = parms->parm.output.timeperframe.numerator; int framerate = m_videoParams.frameRate.frameRateNum/m_videoParams.frameRate.frameRateDenom; if (framerate * 2 < m_videoParams.intraPeriod) { m_videoParams.intraPeriod = framerate * 2; } m_videoParamsChanged = true; } UpdateVideoParameters(); } break; case VIDIOC_S_FMT: { struct v4l2_format *format = static_cast<struct v4l2_format *>(arg); ASSERT(!m_streamOn[INPUT] && !m_streamOn[OUTPUT]); if (format->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { // ::SetOutputFormat switch (format->fmt.pix_mp.pixelformat) { case V4L2_PIX_FMT_H264: { m_encoder.reset(createVideoEncoder("video/h264"), releaseVideoEncoder); m_videoParams.size = sizeof(m_videoParams); encodeStatus = m_encoder->getParameters(VideoParamsTypeCommon, &m_videoParams); ASSERT(encodeStatus == ENCODE_SUCCESS); m_videoParams.profile = VAProfileH264Main; encodeStatus = m_encoder->setParameters(VideoParamsTypeCommon, &m_videoParams); ASSERT(encodeStatus == ENCODE_SUCCESS); } break; case V4L2_PIX_FMT_VP8: default: ret = -1; break; } } else if (format->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { // ::NegotiateInputFormat ASSERT(format->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_YUV420M || format->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_YUYV); m_pixelFormat[INPUT] = format->fmt.pix_mp.pixelformat; switch (m_pixelFormat[INPUT]) { case V4L2_PIX_FMT_YUV420M: m_bufferPlaneCount[INPUT] = 3; break; case V4L2_PIX_FMT_YUYV: m_bufferPlaneCount[INPUT] = 1; break; default: ASSERT(0); break; } ASSERT(m_encoder); m_videoParams.resolution.width = format->fmt.pix_mp.width; m_videoParams.resolution.height= format->fmt.pix_mp.height; encodeStatus = m_encoder->setParameters(VideoParamsTypeCommon, &m_videoParams); ASSERT(encodeStatus == ENCODE_SUCCESS); encodeStatus = m_encoder->getMaxOutSize(&m_maxOutputBufferSize); ASSERT(encodeStatus == ENCODE_SUCCESS); INFO("resolution: %d x %d, m_maxOutputBufferSize: %d", m_videoParams.resolution.width, m_videoParams.resolution.height, m_maxOutputBufferSize); format->fmt.pix_mp.plane_fmt[0].bytesperline = m_videoParams.resolution.width; format->fmt.pix_mp.plane_fmt[0].sizeimage = m_videoParams.resolution.width * m_videoParams.resolution.height; format->fmt.pix_mp.plane_fmt[1].bytesperline = m_videoParams.resolution.width/2; format->fmt.pix_mp.plane_fmt[1].sizeimage = m_videoParams.resolution.width * m_videoParams.resolution.height /4; format->fmt.pix_mp.plane_fmt[2].bytesperline = m_videoParams.resolution.width/2; format->fmt.pix_mp.plane_fmt[2].sizeimage = m_videoParams.resolution.width * m_videoParams.resolution.height /4; } else { ret = -1; ERROR("unknow type: %d of setting format VIDIOC_S_FMT", format->type); } } break; case VIDIOC_S_CROP: { // ::SetFormats struct v4l2_crop *crop = static_cast<struct v4l2_crop *>(arg); INFO("ignore crop for now (the difference between buffer size and real size)"); } break; default: ret = -1; ERROR("unknown ioctrl command: %d", command); break; } if (ret == -1 && errno != EAGAIN) { ERROR("ioctl failed"); WARNING("ioctl command: %s failed", IoctlCommandString(command)); } return ret; }