static void dec_process(MSFilter *f){ DecData *d=(DecData*)f->data; mblk_t *im; MSQueue nalus; AVFrame orig; ms_queue_init(&nalus); while((im=ms_queue_get(f->inputs[0]))!=NULL){ /*push the sps/pps given in sprop-parameter-sets if any*/ if (d->packet_num==0 && d->sps && d->pps){ mblk_set_timestamp_info(d->sps,mblk_get_timestamp_info(im)); mblk_set_timestamp_info(d->pps,mblk_get_timestamp_info(im)); rfc3984_unpack(&d->unpacker,d->sps,&nalus); rfc3984_unpack(&d->unpacker,d->pps,&nalus); d->sps=NULL; d->pps=NULL; } rfc3984_unpack(&d->unpacker,im,&nalus); if (!ms_queue_empty(&nalus)){ int size; uint8_t *p,*end; bool_t need_reinit=FALSE; size=nalusToFrame(d,&nalus,&need_reinit); if (need_reinit) dec_reinit(d); p=d->bitstream; end=d->bitstream+size; while (end-p>0) { int len; int got_picture=0; AVPacket pkt; avcodec_get_frame_defaults(&orig); av_init_packet(&pkt); pkt.data = p; pkt.size = end-p; len=avcodec_decode_video2(&d->av_context,&orig,&got_picture,&pkt); if (len<=0) { ms_warning("ms_AVdecoder_process: error %i.",len); if ((f->ticker->time - d->last_error_reported_time)>5000 || d->last_error_reported_time==0) { d->last_error_reported_time=f->ticker->time; ms_filter_notify_no_arg(f,MS_VIDEO_DECODER_DECODING_ERRORS); } break; } if (got_picture) { ms_queue_put(f->outputs[0],get_as_yuvmsg(f,d,&orig)); if (!d->first_image_decoded) { ms_filter_notify_no_arg(f,MS_VIDEO_DECODER_FIRST_IMAGE_DECODED); d->first_image_decoded = TRUE; } } p+=len; } } d->packet_num++; } }
/* remove payload header and aggregates fragmented packets */ static void dec_unpacketize(MSFilter *f, DecState *s, mblk_t *im, MSQueue *out){ int xbit = (im->b_rptr[0] & 0x80) >> 7; im->b_rptr++; if (xbit) { /* Ignore extensions if some are present */ int ibit = (im->b_rptr[0] & 0x80) >> 7; int lbit = (im->b_rptr[0] & 0x40) >> 6; int tbit = (im->b_rptr[0] & 0x20) >> 5; int kbit = (im->b_rptr[0] & 0x10) >> 4; int mbit = 0; if (ibit) { mbit = (im->b_rptr[1] & 0x80) >> 7; } im->b_rptr += (ibit + lbit + (tbit | kbit) + mbit); } /* end of frame bit ? */ if (mblk_get_marker_info(im)) { /* should be aggregated with previous packet ? */ if (s->curframe!=NULL){ /* same timestamp ? */ if (mblk_get_timestamp_info(im) == mblk_get_timestamp_info(s->curframe)) { concatb(s->curframe,im); msgpullup(s->curframe,-1); /* transmit complete frame */ ms_queue_put(out, s->curframe); s->curframe=NULL; } else { /* transmit partial frame */ ms_queue_put(out, s->curframe); s->curframe = NULL; /* transmit new one (be it complete or not) */ ms_queue_put(out, im); } } else { /* transmit new one (be it complete or not) */ ms_queue_put(out, im); } } else { if (s->curframe!=NULL) { /* append if same timestamp */ if (mblk_get_timestamp_info(im) == mblk_get_timestamp_info(s->curframe)) { concatb(s->curframe,im); } else { /* transmit partial frame */ ms_queue_put(out, s->curframe); s->curframe = im; } } else { s->curframe = im; } } }
/* the goal of that function is to return a absolute timestamp closest to real time, with respect of given packet_ts, which is a relative to an undefined origin*/ static uint32_t get_cur_timestamp(MSFilter * f, mblk_t *im) { SenderData *d = (SenderData *) f->data; uint32_t curts = (uint32_t)( (f->ticker->time*(uint64_t)d->rate)/(uint64_t)1000) ; int diffts; uint32_t netts; int difftime_ts; if (im && d->dtmf==0){ /*do not perform timestamp adjustment while a dtmf is being sent, otherwise durations are erroneous */ uint32_t packet_ts=mblk_get_timestamp_info(im); if (d->last_sent_time==-1){ d->tsoff = curts - packet_ts; }else{ diffts=packet_ts-d->last_ts; difftime_ts=((f->ticker->time-d->last_sent_time)*d->rate)/1000; /* detect timestamp jump in the stream and adjust so that they become continuous on the network*/ if (abs(diffts-difftime_ts)>(d->rate/5)){ uint32_t tsoff=curts - packet_ts; ms_message("Adjusting output timestamp by %i",(tsoff-d->tsoff)); d->tsoff = tsoff; } } netts = packet_ts + d->tsoff; d->last_sent_time=f->ticker->time; d->last_ts=packet_ts; }else netts=curts; return netts; }
static void sender_process(MSFilter * f) { SenderData *d = (SenderData *) f->data; RtpSession *s = d->session; mblk_t *im; uint32_t timestamp; if (s == NULL){ ms_queue_flush(f->inputs[0]); return; } if (d->relay_session_id_size>0 && ( (f->ticker->time-d->last_rsi_time)>5000 || d->last_rsi_time==0) ) { ms_message("relay session id sent in RTCP APP"); rtp_session_send_rtcp_APP(s,0,"RSID",(const uint8_t *)d->relay_session_id,d->relay_session_id_size); d->last_rsi_time=f->ticker->time; } while ((im = ms_queue_get(f->inputs[0])) != NULL) { mblk_t *header; timestamp = get_cur_timestamp(f, mblk_get_timestamp_info(im)); ms_filter_lock(f); if (d->skip) { ms_debug("skipping.."); send_dtmf(f, d->skip_until-d->dtmf_duration, timestamp); d->dtmf_start = FALSE; if (!RTP_TIMESTAMP_IS_NEWER_THAN(timestamp, d->skip_until)) { freemsg(im); ms_filter_unlock(f); continue; } d->skip = FALSE; d->dtmf = 0; } if (d->skip == FALSE && d->mute_mic==FALSE){ int pt = mblk_get_payload_type(im); header = rtp_session_create_packet(s, 12, NULL, 0); if (pt>0) rtp_set_payload_type(header, pt); rtp_set_markbit(header, mblk_get_marker_info(im)); header->b_cont = im; rtp_session_sendm_with_ts(s, header, timestamp); } else{ freemsg(im); } if (d->dtmf != 0) { ms_debug("prepare to send RFC2833 dtmf."); d->skip_until = timestamp + d->dtmf_duration; d->skip = TRUE; d->dtmf_start = TRUE; } ms_filter_unlock(f); } }
void SinkBase::cb_data(mblk_t *m) { uint32_t st = mblk_get_timestamp_info(m); mblk_t *fm = dupmsg(m); mblk_meta_copy(m, fm); MSQueue *queue = post_handle(fm); // 此处 dupmsg(m) 将不会导致 m 被释放 bool first = true; unsigned char *obuf = 0; size_t olen = 0; int index = 0; size_t off = 0; bool key = false; while (mblk_t *om = ms_queue_get(queue)) { int dlen = size_for(index, om); obuf = (unsigned char*)realloc(obuf, off + dlen); save_for(index, om, obuf + off); if (index == 0) key = is_key(index, om); index++; off += dlen; // 处理时间戳回绕 if (first_frame_) { first_frame_ = false; } else { uint32_t delta = st - last_stamp_; // 检查,是否乱序,乱序包直接扔掉! if (delta > 0x80000000) { fprintf(stderr, "??? maybe timestamp confusioned!, curr=%u, last=%u\n", st, last_stamp_); return; } next_stamp_ += delta / payload_freq(); } last_stamp_ = st; freemsg(om); } if (cb_data_ && obuf) { cb_data_(opaque_, next_stamp_, obuf, off, key); } if (obuf) free(obuf); }
/* remove payload header and aggregates fragmented packets */ static void dec_unpacketize(MSFilter *f, DecState *s, mblk_t *im, MSQueue *out){ im->b_rptr++; /* end of frame bit ? */ if (mblk_get_marker_info(im)) { /* should be aggregated with previous packet ? */ if (s->curframe!=NULL){ /* same timestamp ? */ if (mblk_get_timestamp_info(im) == mblk_get_timestamp_info(s->curframe)) { concatb(s->curframe,im); msgpullup(s->curframe,-1); /* transmit complete frame */ ms_queue_put(out, s->curframe); s->curframe=NULL; } else { /* transmit partial frame */ ms_queue_put(out, s->curframe); s->curframe = NULL; /* transmit new one (be it complete or not) */ ms_queue_put(out, im); } } else { /* transmit new one (be it complete or not) */ ms_queue_put(out, im); } } else { if (s->curframe!=NULL) { /* append if same timestamp */ if (mblk_get_timestamp_info(im) == mblk_get_timestamp_info(s->curframe)) { concatb(s->curframe,im); } else { /* transmit partial frame */ ms_queue_put(out, s->curframe); s->curframe = im; } } else { s->curframe = im; } } }
static void dec_process(MSFilter *f) { DecData *d = (DecData*)f->data; mblk_t *im = NULL; MSQueue nalus; mblk_t *decodeM = NULL; int in_size = 0; jint out_size = 0; mblk_t *oneNalu = NULL; JNIEnv *jni_env = NULL; JavaVM *jvm = ms_get_jvm(); static char start_code[4] = {0, 0, 0, 1}; (*jvm)->AttachCurrentThread(jvm, &jni_env, NULL); ms_queue_init(&nalus); while((im = ms_queue_get(f->inputs[0])) != NULL) { /*push the sps/pps given in sprop-parameter-sets if any*/ if (d->packet_num == 0 && d->sps && d->pps) { mblk_set_timestamp_info(d->sps,mblk_get_timestamp_info(im)); mblk_set_timestamp_info(d->pps,mblk_get_timestamp_info(im)); rfc3984_unpack(&d->unpacker,d->sps,&nalus); rfc3984_unpack(&d->unpacker,d->pps,&nalus); d->sps = NULL; d->pps = NULL; } rfc3984_unpack(&d->unpacker,im,&nalus); while((oneNalu = ms_queue_get(&nalus)) != NULL) { in_size = oneNalu->b_wptr - oneNalu->b_rptr; if (GET_TYPE(oneNalu->b_rptr[0]) == SPS_TYPE) { if (d->sps == NULL) { FMS_WARN("dec_process get sps\n"); d->sps = allocb(in_size + START_CODE_LEN, 0); if (d->sps) { memcpy(d->sps->b_rptr, start_code, START_CODE_LEN); memcpy(d->sps->b_rptr + START_CODE_LEN, oneNalu->b_rptr, in_size); d->sps->b_wptr += in_size + START_CODE_LEN; } } else { freemsg(oneNalu); continue; } } else if (GET_TYPE(oneNalu->b_rptr[0]) == PPS_TYPE) { if (d->pps == NULL && d->sps != NULL) { FMS_WARN("dec_process get pps\n"); d->pps = allocb(in_size + START_CODE_LEN, 0); if (d->pps) { memcpy(d->pps->b_rptr, start_code, START_CODE_LEN); memcpy(d->pps->b_rptr + START_CODE_LEN, oneNalu->b_rptr, in_size); d->pps->b_wptr += in_size + START_CODE_LEN; } } else { freemsg(oneNalu); continue; } } if (d->sps == NULL || (GET_TYPE(oneNalu->b_rptr[0]) != SPS_TYPE && d->pps == NULL)) { FMS_WARN("skip frame without no sps and pps\n"); freemsg(oneNalu); continue; } if (!d->IsRecivedFirstIframe && GET_TYPE(oneNalu->b_rptr[0]) != SPS_TYPE && GET_TYPE(oneNalu->b_rptr[0]) != PPS_TYPE) { if (GET_TYPE(oneNalu->b_rptr[0]) == IDR_TYPE) { d->IsRecivedFirstIframe = TRUE; } else { FMS_WARN("skip frame without the first IDR\n"); freemsg(oneNalu); continue; } } (*jni_env)->SetByteArrayRegion(jni_env, d->input_data, 0, START_CODE_LEN, (jbyte*)start_code); (*jni_env)->SetByteArrayRegion(jni_env, d->input_data, START_CODE_LEN, in_size, (jbyte*)oneNalu->b_rptr); out_size = (*jni_env)->CallIntMethod(jni_env, d->h264_decode_obj, d->h264_decode_id, d->input_data, in_size + START_CODE_LEN, d->output_data); if (out_size <= 0) { freemsg(oneNalu); continue; } (*jni_env)->GetByteArrayRegion(jni_env, d->output_data, 0, out_size, (jbyte*)d->bitstream); if (FALSE == d->IsFirstImageDec) { d->IsFirstImageDec = TRUE; //ms_filter_notify_no_arg(f, MS_VIDEO_DECODER_GET_FIRST_VIDEO_FRAME); } decodeM = get_as_yuvmsg(f, d, d->bitstream, out_size); if (decodeM) { ms_queue_put(f->outputs[0], decodeM); } freemsg(oneNalu); } d->packet_num++; } (*jvm)->DetachCurrentThread(jvm); }
static void dec_process(MSFilter *f){ DecData *d=(DecData*)f->data; MSPicture pic = {0}; mblk_t *im,*om = NULL; ssize_t oBufidx = -1; size_t bufsize; bool_t need_reinit=FALSE; bool_t request_pli=FALSE; MSQueue nalus; AMediaCodecBufferInfo info; ms_queue_init(&nalus); while((im=ms_queue_get(f->inputs[0]))!=NULL){ if (d->packet_num==0 && d->sps && d->pps){ mblk_set_timestamp_info(d->sps,mblk_get_timestamp_info(im)); mblk_set_timestamp_info(d->pps,mblk_get_timestamp_info(im)); rfc3984_unpack(&d->unpacker, d->sps, &nalus); rfc3984_unpack(&d->unpacker, d->pps, &nalus); d->sps=NULL; d->pps=NULL; } if(rfc3984_unpack(&d->unpacker,im,&nalus) <0){ request_pli=TRUE; } if (!ms_queue_empty(&nalus)){ int size; uint8_t *buf=NULL; ssize_t iBufidx; size=nalusToFrame(d,&nalus,&need_reinit); if (need_reinit) { //In case of rotation, the decoder needs to flushed in order to restart with the new video size AMediaCodec_flush(d->codec); d->first_buffer_queued = FALSE; } /*First put our H264 bitstream into the decoder*/ iBufidx = AMediaCodec_dequeueInputBuffer(d->codec, TIMEOUT_US); if (iBufidx >= 0) { buf = AMediaCodec_getInputBuffer(d->codec, iBufidx, &bufsize); if(buf == NULL) { ms_error("MSMediaCodecH264Dec: AMediaCodec_getInputBuffer() returned NULL"); break; } if((size_t)size > bufsize) { ms_error("Cannot copy the bitstream into the input buffer size : %i and bufsize %i",size,(int) bufsize); break; } else { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); memcpy(buf,d->bitstream,(size_t)size); AMediaCodec_queueInputBuffer(d->codec, iBufidx, 0, (size_t)size, (ts.tv_nsec/1000) + 10000LL, 0); d->first_buffer_queued = TRUE; } }else if (iBufidx == AMEDIA_ERROR_UNKNOWN){ ms_error("MSMediaCodecH264Dec: AMediaCodec_dequeueInputBuffer() had an exception"); } } d->packet_num++; if (d->sps && d->pps) request_pli = FALSE; else request_pli = TRUE; } /*secondly try to get decoded frames from the decoder, this is performed every tick*/ while (d->first_buffer_queued && (oBufidx = AMediaCodec_dequeueOutputBuffer(d->codec, &info, TIMEOUT_US)) >= 0){ AMediaFormat *format; int width = 0, height = 0, color = 0; uint8_t *buf = AMediaCodec_getOutputBuffer(d->codec, oBufidx, &bufsize); if(buf == NULL){ ms_filter_notify_no_arg(f,MS_VIDEO_DECODER_DECODING_ERRORS); ms_error("MSMediaCodecH264Dec: AMediaCodec_getOutputBuffer() returned NULL"); } format = AMediaCodec_getOutputFormat(d->codec); if(format != NULL){ AMediaFormat_getInt32(format, "width", &width); AMediaFormat_getInt32(format, "height", &height); AMediaFormat_getInt32(format, "color-format", &color); d->vsize.width=width; d->vsize.height=height; AMediaFormat_delete(format); } if(buf != NULL && d->sps && d->pps){ /*some decoders output garbage while no sps or pps have been received yet !*/ if(width != 0 && height != 0 ){ if(color == 19) { //YUV int ysize = width*height; int usize = ysize/4; om = ms_yuv_buf_allocator_get(d->buf_allocator,&pic,width,height); memcpy(pic.planes[0],buf,ysize); memcpy(pic.planes[1],buf+ysize,usize); memcpy(pic.planes[2],buf+ysize+usize,usize); } else { uint8_t* cbcr_src = (uint8_t*) (buf + width * height); om = copy_ycbcrbiplanar_to_true_yuv_with_rotation_and_down_scale_by_2(d->buf_allocator, buf, cbcr_src, 0, width, height, width, width, TRUE, FALSE); } if (!d->first_image_decoded) { ms_message("First frame decoded %ix%i",width,height); d->first_image_decoded = true; ms_filter_notify_no_arg(f, MS_VIDEO_DECODER_FIRST_IMAGE_DECODED); } ms_queue_put(f->outputs[0], om); }else{ ms_error("MSMediaCodecH264Dec: width and height are not known !"); } } AMediaCodec_releaseOutputBuffer(d->codec, oBufidx, FALSE); } if (oBufidx == AMEDIA_ERROR_UNKNOWN){ ms_error("MSMediaCodecH264Dec: AMediaCodec_dequeueOutputBuffer() had an exception"); } if (d->avpf_enabled && request_pli) { ms_filter_notify_no_arg(f, MS_VIDEO_DECODER_SEND_PLI); } ms_queue_flush(f->inputs[0]); }
void MSOpenH264Decoder::feed() { if (!isInitialized()) { ms_error("MSOpenH264Decoder::feed(): not initialized"); ms_queue_flush(mFilter->inputs[0]); return; } MSQueue nalus; ms_queue_init(&nalus); mblk_t *im; while ((im = ms_queue_get(mFilter->inputs[0])) != NULL) { if ((getIDRPicId() == 0) && (mSPS != 0) && (mPPS != 0)) { // Push the sps/pps given in sprop-parameter-sets if any mblk_set_timestamp_info(mSPS, mblk_get_timestamp_info(im)); mblk_set_timestamp_info(mPPS, mblk_get_timestamp_info(im)); rfc3984_unpack(mUnpacker, mSPS, &nalus); rfc3984_unpack(mUnpacker, mPPS, &nalus); mSPS = 0; mPPS = 0; } rfc3984_unpack(mUnpacker, im, &nalus); if (!ms_queue_empty(&nalus)) { void * pData[3] = { 0 }; SBufferInfo sDstBufInfo = { 0 }; int len = nalusToFrame(&nalus); DECODING_STATE state = mDecoder->DecodeFrame2(mBitstream, len, (uint8_t**)pData, &sDstBufInfo); if (state != dsErrorFree) { ms_error("OpenH264 decoder: DecodeFrame2 failed: 0x%x", state); if (((mFilter->ticker->time - mLastErrorReportTime) > 5000) || (mLastErrorReportTime == 0)) { mLastErrorReportTime = mFilter->ticker->time; ms_filter_notify_no_arg(mFilter, MS_VIDEO_DECODER_DECODING_ERRORS); } } if (sDstBufInfo.iBufferStatus == 1) { uint8_t * pDst[3] = { 0 }; pDst[0] = (uint8_t *)pData[0]; pDst[1] = (uint8_t *)pData[1]; pDst[2] = (uint8_t *)pData[2]; // Update video size and (re)allocate YUV buffer if needed if ((mWidth != sDstBufInfo.UsrData.sSystemBuffer.iWidth) || (mHeight != sDstBufInfo.UsrData.sSystemBuffer.iHeight)) { if (mYUVMsg) { freemsg(mYUVMsg); } mWidth = sDstBufInfo.UsrData.sSystemBuffer.iWidth; mHeight = sDstBufInfo.UsrData.sSystemBuffer.iHeight; mYUVMsg = ms_yuv_buf_alloc(&mOutbuf, mWidth, mHeight); ms_filter_notify_no_arg(mFilter,MS_FILTER_OUTPUT_FMT_CHANGED); } // Scale/copy frame to destination mblk_t for (int i = 0; i < 3; i++) { uint8_t *dst = mOutbuf.planes[i]; uint8_t *src = pDst[i]; int h = mHeight >> (( i > 0) ? 1 : 0); for(int j = 0; j < h; j++) { memcpy(dst, src, mOutbuf.strides[i]); dst += mOutbuf.strides[i]; src += sDstBufInfo.UsrData.sSystemBuffer.iStride[(i == 0) ? 0 : 1]; } } ms_queue_put(mFilter->outputs[0], dupmsg(mYUVMsg)); // Update average FPS if (ms_average_fps_update(&mFPS, mFilter->ticker->time)) { ms_message("OpenH264 decoder: Frame size: %dx%d", mWidth, mHeight); } // Notify first decoded image if (!mFirstImageDecoded) { mFirstImageDecoded = true; ms_filter_notify_no_arg(mFilter, MS_VIDEO_DECODER_FIRST_IMAGE_DECODED); } #if MSOPENH264_DEBUG ms_message("OpenH264 decoder: IDR pic id: %d, Frame num: %d, Temporal id: %d, VCL NAL: %d", getIDRPicId(), getFrameNum(), getTemporalId(), getVCLNal()); #endif } }
static void dec_process(MSFilter *f){ DecData *d=(DecData*)f->data; MSPicture pic = {0}; mblk_t *im,*om = NULL; bool_t need_reinit=FALSE; bool_t request_pli=FALSE; MSQueue nalus; ms_queue_init(&nalus); while((im=ms_queue_get(f->inputs[0]))!=NULL){ if (d->packet_num==0 && d->sps && d->pps){ mblk_set_timestamp_info(d->sps,mblk_get_timestamp_info(im)); mblk_set_timestamp_info(d->pps,mblk_get_timestamp_info(im)); rfc3984_unpack(&d->unpacker, d->sps, &nalus); rfc3984_unpack(&d->unpacker, d->pps, &nalus); d->sps=NULL; d->pps=NULL; } if(rfc3984_unpack(&d->unpacker,im,&nalus) <0){ request_pli=TRUE; } if (!ms_queue_empty(&nalus)){ AMediaCodecBufferInfo info; int size; int width = 0, height = 0, color = 0; uint8_t *buf=NULL; size_t bufsize; ssize_t iBufidx, oBufidx; size=nalusToFrame(d,&nalus,&need_reinit); if (need_reinit) { //In case of rotation, the decoder needs to flushed in order to restart with the new video size AMediaCodec_flush(d->codec); } iBufidx = AMediaCodec_dequeueInputBuffer(d->codec, TIMEOUT_US); if (iBufidx >= 0) { buf = AMediaCodec_getInputBuffer(d->codec, iBufidx, &bufsize); if(buf == NULL) { break; } if((size_t)size > bufsize) { ms_error("Cannot copy the bitstream into the input buffer size : %i and bufsize %i",size,(int) bufsize); } else { memcpy(buf,d->bitstream,(size_t)size); AMediaCodec_queueInputBuffer(d->codec, iBufidx, 0, (size_t)size, TIMEOUT_US, 0); } } oBufidx = AMediaCodec_dequeueOutputBuffer(d->codec, &info, TIMEOUT_US); if(oBufidx >= 0){ AMediaFormat *format; buf = AMediaCodec_getOutputBuffer(d->codec, oBufidx, &bufsize); if(buf == NULL){ ms_filter_notify_no_arg(f,MS_VIDEO_DECODER_DECODING_ERRORS); break; } format = AMediaCodec_getOutputFormat(d->codec); if(format != NULL){ AMediaFormat_getInt32(format, "width", &width); AMediaFormat_getInt32(format, "height", &height); AMediaFormat_getInt32(format, "color-format", &color); d->vsize.width=width; d->vsize.height=height; AMediaFormat_delete(format); } } if(buf != NULL){ //YUV if(width != 0 && height != 0 ){ if(color == 19) { int ysize = width*height; int usize = ysize/4; om=ms_yuv_buf_alloc(&pic,width,height); memcpy(pic.planes[0],buf,ysize); memcpy(pic.planes[1],buf+ysize,usize); memcpy(pic.planes[2],buf+ysize+usize,usize); } else { uint8_t* cbcr_src = (uint8_t*) (buf + width * height); om = copy_ycbcrbiplanar_to_true_yuv_with_rotation_and_down_scale_by_2(d->buf_allocator, buf, cbcr_src, 0, width, height, width, width, TRUE, FALSE); } if (!d->first_image_decoded) { ms_message("First frame decoded %ix%i",width,height); d->first_image_decoded = true; ms_filter_notify_no_arg(f, MS_VIDEO_DECODER_FIRST_IMAGE_DECODED); } ms_queue_put(f->outputs[0], om); } if(oBufidx > 0) { AMediaCodec_releaseOutputBuffer(d->codec, oBufidx, FALSE); } } } d->packet_num++; } if (d->avpf_enabled && request_pli) { ms_filter_notify_no_arg(f, MS_VIDEO_DECODER_SEND_PLI); } }