/* API: Get frame from stream */ static pj_status_t ffmpeg_stream_get_frame(pjmedia_vid_dev_stream *s, pjmedia_frame *frame) { ffmpeg_stream *strm = (ffmpeg_stream*)s; AVPacket p; int err; err = av_read_frame(strm->ff_fmt_ctx, &p); if (err < 0) { print_ffmpeg_err(err); return PJ_EUNKNOWN; } pj_bzero(frame, sizeof(*frame)); frame->type = PJMEDIA_FRAME_TYPE_VIDEO; frame->buf = p.data; frame->size = p.size; return PJ_SUCCESS; }
static pj_status_t ffmpeg_capture_open(AVFormatContext **ctx, AVInputFormat *ifmt, const char *dev_name, const pjmedia_vid_dev_param *param) { AVFormatParameters fp; pjmedia_video_format_detail *vfd; int err; PJ_ASSERT_RETURN(ctx && ifmt && dev_name && param, PJ_EINVAL); PJ_ASSERT_RETURN(param->fmt.detail_type == PJMEDIA_FORMAT_DETAIL_VIDEO, PJ_EINVAL); vfd = pjmedia_format_get_video_format_detail(¶m->fmt, PJ_TRUE); /* Init ffmpeg format context */ *ctx = avformat_alloc_context(); /* Init ffmpeg format param */ pj_bzero(&fp, sizeof(fp)); fp.prealloced_context = 1; fp.width = vfd->size.w; fp.height = vfd->size.h; fp.pix_fmt = PIX_FMT_BGR24; fp.time_base.num = vfd->fps.denum; fp.time_base.den = vfd->fps.num; /* Open capture stream */ err = av_open_input_stream(ctx, NULL, dev_name, ifmt, &fp); if (err < 0) { *ctx = NULL; /* ffmpeg freed its states on failure, do we must too */ print_ffmpeg_err(err); return PJ_EUNKNOWN; } return PJ_SUCCESS; }
/* * Encode frames. */ static pj_status_t ffmpeg_codec_encode_whole(pjmedia_vid_codec *codec, const pjmedia_vid_encode_opt *opt, const pjmedia_frame *input, unsigned output_buf_len, pjmedia_frame *output) { ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data; pj_uint8_t *p = (pj_uint8_t*)input->buf; AVFrame avframe; pj_uint8_t *out_buf = (pj_uint8_t*)output->buf; int out_buf_len = output_buf_len; int err; //AVRational src_timebase; /* For some reasons (e.g: SSE/MMX usage), the avcodec_encode_video() must * have stack aligned to 16 bytes. Let's try to be safe by preparing the * 16-bytes aligned stack here, in case it's not managed by the ffmpeg. */ PJ_ALIGN_DATA(pj_uint32_t i[4], 16); if ((long)i & 0xF) { PJ_LOG(2,(THIS_FILE, "Stack alignment fails")); } /* Check if encoder has been opened */ PJ_ASSERT_RETURN(ff->enc_ctx, PJ_EINVALIDOP); avcodec_get_frame_defaults(&avframe); // Let ffmpeg manage the timestamps /* src_timebase.num = 1; src_timebase.den = ff->desc->info.clock_rate; avframe.pts = av_rescale_q(input->timestamp.u64, src_timebase, ff->enc_ctx->time_base); */ for (i[0] = 0; i[0] < ff->enc_vfi->plane_cnt; ++i[0]) { avframe.data[i[0]] = p; avframe.linesize[i[0]] = ff->enc_vafp.strides[i[0]]; p += ff->enc_vafp.plane_bytes[i[0]]; } /* Force keyframe */ if (opt && opt->force_keyframe) { #if LIBAVCODEC_VER_AT_LEAST(53,20) avframe.pict_type = AV_PICTURE_TYPE_I; #else avframe.pict_type = FF_I_TYPE; #endif } err = avcodec_encode_video(ff->enc_ctx, out_buf, out_buf_len, &avframe); if (err < 0) { print_ffmpeg_err(err); return PJMEDIA_CODEC_EFAILED; } else { output->size = err; output->bit_info = 0; if (ff->enc_ctx->coded_frame->key_frame) output->bit_info |= PJMEDIA_VID_FRM_KEYFRAME; } return PJ_SUCCESS; }
/* * Decode frame. */ static pj_status_t ffmpeg_codec_decode_whole(pjmedia_vid_codec *codec, const pjmedia_frame *input, unsigned output_buf_len, pjmedia_frame *output) { ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data; AVFrame avframe; AVPacket avpacket; int err, got_picture; /* Check if decoder has been opened */ PJ_ASSERT_RETURN(ff->dec_ctx, PJ_EINVALIDOP); /* Reset output frame bit info */ output->bit_info = 0; /* Validate output buffer size */ // Do this validation later after getting decoding result, where the real // decoded size will be assured. //if (ff->dec_vafp.framebytes > output_buf_len) //return PJ_ETOOSMALL; /* Init frame to receive the decoded data, the ffmpeg codec context will * automatically provide the decoded buffer (single buffer used for the * whole decoding session, and seems to be freed when the codec context * closed). */ avcodec_get_frame_defaults(&avframe); /* Init packet, the container of the encoded data */ av_init_packet(&avpacket); avpacket.data = (pj_uint8_t*)input->buf; avpacket.size = input->size; /* ffmpeg warns: * - input buffer padding, at least FF_INPUT_BUFFER_PADDING_SIZE * - null terminated * Normally, encoded buffer is allocated more than needed, so lets just * bzero the input buffer end/pad, hope it will be just fine. */ pj_bzero(avpacket.data+avpacket.size, FF_INPUT_BUFFER_PADDING_SIZE); output->bit_info = 0; output->timestamp = input->timestamp; #if LIBAVCODEC_VER_AT_LEAST(52,72) //avpacket.flags = AV_PKT_FLAG_KEY; #else avpacket.flags = 0; #endif #if LIBAVCODEC_VER_AT_LEAST(52,72) err = avcodec_decode_video2(ff->dec_ctx, &avframe, &got_picture, &avpacket); #else err = avcodec_decode_video(ff->dec_ctx, &avframe, &got_picture, avpacket.data, avpacket.size); #endif if (err < 0) { pjmedia_event event; output->type = PJMEDIA_FRAME_TYPE_NONE; output->size = 0; print_ffmpeg_err(err); /* Broadcast missing keyframe event */ pjmedia_event_init(&event, PJMEDIA_EVENT_KEYFRAME_MISSING, &input->timestamp, codec); pjmedia_event_publish(NULL, codec, &event, 0); return PJMEDIA_CODEC_EBADBITSTREAM; } else if (got_picture) { pjmedia_video_apply_fmt_param *vafp = &ff->dec_vafp; pj_uint8_t *q = (pj_uint8_t*)output->buf; unsigned i; pj_status_t status; /* Check decoding result, e.g: see if the format got changed, * keyframe found/missing. */ status = check_decode_result(codec, &input->timestamp, avframe.key_frame); if (status != PJ_SUCCESS) return status; /* Check provided buffer size */ if (vafp->framebytes > output_buf_len) return PJ_ETOOSMALL; /* Get the decoded data */ for (i = 0; i < ff->dec_vfi->plane_cnt; ++i) { pj_uint8_t *p = avframe.data[i]; /* The decoded data may contain padding */ if (avframe.linesize[i]!=vafp->strides[i]) { /* Padding exists, copy line by line */ pj_uint8_t *q_end; q_end = q+vafp->plane_bytes[i]; while(q < q_end) { pj_memcpy(q, p, vafp->strides[i]); q += vafp->strides[i]; p += avframe.linesize[i]; } } else { /* No padding, copy the whole plane */ pj_memcpy(q, p, vafp->plane_bytes[i]); q += vafp->plane_bytes[i]; } } output->type = PJMEDIA_FRAME_TYPE_VIDEO; output->size = vafp->framebytes; } else { output->type = PJMEDIA_FRAME_TYPE_NONE; output->size = 0; } return PJ_SUCCESS; }
static pj_status_t open_ffmpeg_codec(ffmpeg_private *ff, pj_mutex_t *ff_mutex) { enum PixelFormat pix_fmt; pjmedia_video_format_detail *vfd; pj_bool_t enc_opened = PJ_FALSE, dec_opened = PJ_FALSE; pj_status_t status; /* Get decoded pixel format */ status = pjmedia_format_id_to_PixelFormat(ff->param.dec_fmt.id, &pix_fmt); if (status != PJ_SUCCESS) return status; ff->expected_dec_fmt = pix_fmt; /* Get video format detail for shortcut access to encoded format */ vfd = pjmedia_format_get_video_format_detail(&ff->param.enc_fmt, PJ_TRUE); /* Allocate ffmpeg codec context */ if (ff->param.dir & PJMEDIA_DIR_ENCODING) { #if LIBAVCODEC_VER_AT_LEAST(53,20) ff->enc_ctx = avcodec_alloc_context3(ff->enc); #else ff->enc_ctx = avcodec_alloc_context(); #endif if (ff->enc_ctx == NULL) goto on_error; } if (ff->param.dir & PJMEDIA_DIR_DECODING) { #if LIBAVCODEC_VER_AT_LEAST(53,20) ff->dec_ctx = avcodec_alloc_context3(ff->dec); #else ff->dec_ctx = avcodec_alloc_context(); #endif if (ff->dec_ctx == NULL) goto on_error; } /* Init generic encoder params */ if (ff->param.dir & PJMEDIA_DIR_ENCODING) { AVCodecContext *ctx = ff->enc_ctx; ctx->pix_fmt = pix_fmt; ctx->width = vfd->size.w; ctx->height = vfd->size.h; ctx->time_base.num = vfd->fps.denum; ctx->time_base.den = vfd->fps.num; if (vfd->avg_bps) { ctx->bit_rate = vfd->avg_bps; if (vfd->max_bps > vfd->avg_bps) ctx->bit_rate_tolerance = vfd->max_bps - vfd->avg_bps; } ctx->strict_std_compliance = FF_COMPLIANCE_STRICT; ctx->workaround_bugs = FF_BUG_AUTODETECT; ctx->opaque = ff; /* Set no delay, note that this may cause some codec functionals * not working (e.g: rate control). */ #if LIBAVCODEC_VER_AT_LEAST(52,113) ctx->rc_lookahead = 0; #endif } /* Init generic decoder params */ if (ff->param.dir & PJMEDIA_DIR_DECODING) { AVCodecContext *ctx = ff->dec_ctx; /* Width/height may be overriden by ffmpeg after first decoding. */ ctx->width = ctx->coded_width = ff->param.dec_fmt.det.vid.size.w; ctx->height = ctx->coded_height = ff->param.dec_fmt.det.vid.size.h; ctx->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL; ctx->workaround_bugs = FF_BUG_AUTODETECT; ctx->opaque = ff; } /* Override generic params or apply specific params before opening * the codec. */ if (ff->desc->preopen) { status = (*ff->desc->preopen)(ff); if (status != PJ_SUCCESS) goto on_error; } /* Open encoder */ if (ff->param.dir & PJMEDIA_DIR_ENCODING) { int err; pj_mutex_lock(ff_mutex); err = avcodec_open(ff->enc_ctx, ff->enc); pj_mutex_unlock(ff_mutex); if (err < 0) { print_ffmpeg_err(err); status = PJMEDIA_CODEC_EFAILED; goto on_error; } enc_opened = PJ_TRUE; } /* Open decoder */ if (ff->param.dir & PJMEDIA_DIR_DECODING) { int err; pj_mutex_lock(ff_mutex); err = avcodec_open(ff->dec_ctx, ff->dec); pj_mutex_unlock(ff_mutex); if (err < 0) { print_ffmpeg_err(err); status = PJMEDIA_CODEC_EFAILED; goto on_error; } dec_opened = PJ_TRUE; } /* Let the codec apply specific params after the codec opened */ if (ff->desc->postopen) { status = (*ff->desc->postopen)(ff); if (status != PJ_SUCCESS) goto on_error; } return PJ_SUCCESS; on_error: if (ff->enc_ctx) { if (enc_opened) avcodec_close(ff->enc_ctx); av_free(ff->enc_ctx); ff->enc_ctx = NULL; } if (ff->dec_ctx) { if (dec_opened) avcodec_close(ff->dec_ctx); av_free(ff->dec_ctx); ff->dec_ctx = NULL; } return status; }