// // Created by 阳坤 on 2022/3/1. // #include "av_decode.h" static int openFFmpegDecoderContext(AVFormatContext *fmtCtx, enum AVMediaType type, int *streamIdx, AVCodecContext **decCtx, enum AVCodecID codecId) { if (!fmtCtx)return -1; int ret = -1; int streamIndex = *streamIdx; AVCodecContext *ctx = NULL; AVCodecParameters *parameters = fmtCtx->streams[streamIndex]->codecpar;; AVCodec *codec = avcodec_find_decoder(codecId); if (codec == NULL) { if (codecId == AV_CODEC_ID_PNG) LOGE("openFFmpegDecoderContext avcodec_find_decoder png not found. \n"); LOGE("openFFmpegDecoderContext avcodec_find_decoder=%d not found. \n", codecId); return -1; } ctx = avcodec_alloc_context3(codec); if (!ctx || !parameters || !codec) { LOGE("openFFmpegDecoderContext init error %d ctx==null=%d parameters==null=%d codec==null=%d \n", codecId, ctx == NULL, parameters == NULL, codec == NULL); return -1; } if ((ret = avcodec_parameters_to_context(ctx, parameters)) != 0) { Log("Failed to copy %s codec parameters to decoder context.", av_get_media_type_string(type)); return -1; } switch (type) { case AVMEDIA_TYPE_VIDEO: ctx->framerate = av_guess_frame_rate(fmtCtx, fmtCtx->streams[streamIndex], NULL); break; case AVMEDIA_TYPE_AUDIO: break; } ctx->thread_count = 3; if ((ret = avcodec_open2(ctx, codec, NULL)) != 0) { char buf[512]; av_strerror(ret, buf, 512); LOGE("Failed to open %s codec. error=%s \n", av_get_media_type_string(type), buf); return -1; } *decCtx = ctx; avcodec_flush_buffers(ctx); ret = 0; return ret; } static int av_decode(struct DecoderContext *pContext, AVPacket *pPacket, enum AVMediaType type) { AVCodecContext *ctx = NULL; switch (type) { case AVMEDIA_TYPE_VIDEO: ctx = pContext->videoCodecContext; break; case AVMEDIA_TYPE_AUDIO: ctx = pContext->audioCodecContext; break; } if (!ctx)return -1; int ret = 0; if (pPacket) ret = avcodec_send_packet(ctx, pPacket); else ret = avcodec_send_packet(ctx, NULL); if (ret != 0) { return -1; } while (1) { AVFrame *out_frame = av_frame_alloc(); ret = avcodec_receive_frame(ctx, out_frame); if (ret != 0) { av_frame_free(&out_frame); break; } int64_t pts = -1; if (pPacket) pts = out_frame->pts * (1000 * (av_q2d(pContext->avformatContext->streams[(pPacket)->stream_index]->time_base))); else pts = out_frame->pts * (1000 * (av_q2d(pContext->avformatContext->streams[pContext->st_index[type]]->time_base))); if (pContext->end_time > 0 && pts > pContext->end_time && type == AVMEDIA_TYPE_VIDEO)return -4; if (pContext->end_time > 0 && pts > pContext->end_time && type == AVMEDIA_TYPE_AUDIO)return 0; // LOGE("decode is video=%d pts=%lld \n",pPacket->stream_index == pContext->st_index[AVMEDIA_TYPE_VIDEO],pts); if (pts < pContext->start_time && pContext->start_time >= 0 && pContext->force_Iframe != 1) { continue; } double def_frame_delay = (double) (1000.0 / pContext->fps); //如果持续时间很短,那么就用每帧固定延迟时间 if (out_frame->pkt_duration < 10) { pContext->decode_frame_ms += (long) def_frame_delay; } else {//使用更加精准的帧延迟时间 //根据每帧的延迟时间来相加,更加精准,有些 pkt_duration 持续时长是 0 pContext->decode_frame_ms += (int64_t) (out_frame->pkt_duration * ((AV_TIME_BASE * (av_q2d(pContext->avformatContext->streams[pContext->st_index[AVMEDIA_TYPE_VIDEO]]->time_base))))) / 1000; } out_frame->pts = pts; out_frame->pkt_dts = pts; switch (type) { case AVMEDIA_TYPE_VIDEO: pContext->video_queue->PushBack(out_frame); break; case AVMEDIA_TYPE_AUDIO: pContext->audio_queue->PushBack(out_frame); break; } } return ret; } /** * 初始化解码器 * @param url * @param outVideoFormat * @param force_Iframe * @param end_time * @return */ long initDecoder(const char *url, int force_Iframe, DisableMediaType disableMediaType) { struct DecoderContext *dctx = (struct DecoderContext *) malloc(sizeof(struct DecoderContext)); if (!dctx) { LOGE("DecoderContext create fail."); return -1; } memset(dctx, 0, sizeof(struct DecoderContext)); dctx->url = strdup(url); dctx->force_Iframe = force_Iframe; dctx->ffthread_count = 0; dctx->start_time = 0; dctx->end_time = -1; dctx->disableType = disableMediaType; char err_info[512] = {0}; int ret = avformat_open_input(&(dctx->avformatContext), dctx->url, NULL, NULL); if (ret != 0) { av_strerror(ret, err_info, 512); Log("avformat_open_input failed %d %s. path=%s \n", ret, err_info, dctx->url); close_decoder((long) dctx); return ret; } ret = avformat_find_stream_info(dctx->avformatContext, 0); if (ret < 0) { av_strerror(ret, err_info, 512); Log("Failed to retrieve input stream information msg=%s\n", err_info); close_decoder((long) dctx); return ret; } dctx->st_index[AVMEDIA_TYPE_VIDEO] = av_find_best_stream(dctx->avformatContext, AVMEDIA_TYPE_VIDEO, dctx->st_index[AVMEDIA_TYPE_VIDEO], -1, NULL, 0); if (dctx->st_index[AVMEDIA_TYPE_VIDEO] < 0) { for (int i = 0; i < dctx->avformatContext->nb_streams; ++i) { if (dctx->avformatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) dctx->st_index[AVMEDIA_TYPE_VIDEO] = i; } } dctx->st_index[AVMEDIA_TYPE_AUDIO] = av_find_best_stream(dctx->avformatContext, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0); if (dctx->st_index[AVMEDIA_TYPE_AUDIO] < 0) { for (int i = 0; i < dctx->avformatContext->nb_streams; ++i) { if (dctx->avformatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) dctx->st_index[AVMEDIA_TYPE_AUDIO] = i; } } if (dctx->st_index[AVMEDIA_TYPE_VIDEO] >= 0) { AVStream *as = dctx->avformatContext->streams[dctx->st_index[AVMEDIA_TYPE_VIDEO]]; dctx->v_codec_id = as->codecpar->codec_id; dctx->vformat = as->codecpar->format; dctx->width = as->codecpar->width; dctx->height = as->codecpar->height; dctx->v_bit_rate = as->codecpar->bit_rate; if (dctx->v_bit_rate <= 0 && dctx->avformatContext->bit_rate > 0) { dctx->v_bit_rate = dctx->avformatContext->bit_rate; } else if (dctx->v_bit_rate <= 0) { dctx->v_bit_rate = dctx->width * dctx->height * 3; } dctx->fps = av_q2d(as->avg_frame_rate);//拿到视频码率 dctx->rotate = getVideoRotate(as->metadata); LOGE("找到视频流 width=%d height=%d bit_rate=%d fps=%d rotate=%d\n", dctx->width, dctx->height, dctx->v_bit_rate, dctx->fps, dctx->rotate); } if (dctx->st_index[AVMEDIA_TYPE_AUDIO] >= 0) { AVStream *as = dctx->avformatContext->streams[dctx->st_index[AVMEDIA_TYPE_AUDIO]]; dctx->a_codec_id = as->codecpar->codec_id; dctx->sampleRate = as->codecpar->sample_rate; dctx->channels = as->codecpar->channels; dctx->a_bit_rate = as->codecpar->bit_rate; dctx->aformat = as->codecpar->format; dctx->channels_layout = as->codecpar->channel_layout; LOGE("找到音频流 sampleRate=%d channels=%d bit_rate=%d aformat=%d channels_layout=%d\n", dctx->sampleRate, dctx->channels, dctx->a_bit_rate, dctx->aformat, dctx->channels_layout); } dctx->totalMs = dctx->avformatContext->duration / (AV_TIME_BASE / 1000); if (dctx->start_time > dctx->totalMs) { LOGE("媒体总时长 totalMs=%lld start_time=%lld \n", dctx->totalMs, dctx->start_time); return -1; } LOGE("媒体总时长 totalMs=%lld \n", dctx->totalMs); if (dctx->avformatContext->start_time != AV_NOPTS_VALUE) dctx->offset = dctx->avformatContext->start_time; if (dctx->st_index[AVMEDIA_TYPE_AUDIO] >= 0) { ret = openFFmpegDecoderContext(dctx->avformatContext, AVMEDIA_TYPE_AUDIO, &dctx->st_index[AVMEDIA_TYPE_AUDIO], &dctx->audioCodecContext, dctx->a_codec_id); if (ret != 0) { LOGE("audio openFFmpegDecoderContext error!"); close_decoder((long) dctx); return ret; } } if (dctx->st_index[AVMEDIA_TYPE_VIDEO] >= 0) { ret = openFFmpegDecoderContext(dctx->avformatContext, AVMEDIA_TYPE_VIDEO, &dctx->st_index[AVMEDIA_TYPE_VIDEO], &dctx->videoCodecContext, dctx->v_codec_id); if (ret != 0) { LOGE("video openFFmpegDecoderContext error!"); close_decoder((long) dctx); return ret; } dctx->audio_queue = new BlockQueue(dctx->fps); dctx->video_queue = new BlockQueue(dctx->fps); } seekToMs(dctx); return ret == 0 ? (long) dctx : ret; } void seekToMs(DecoderContext *dctx) { int64_t timestamp = dctx->start_time * 1000; if (dctx->offset != AV_NOPTS_VALUE) timestamp += dctx->offset; if (timestamp > 0) { avformat_flush(dctx->avformatContext); int ret = avformat_seek_file(dctx->avformatContext, -1, INT64_MIN, timestamp, INT64_MAX, AVSEEK_FLAG_BACKWARD); if (ret >= 0) { if (dctx->videoCodecContext) avcodec_flush_buffers(dctx->videoCodecContext); if (dctx->audioCodecContext) avcodec_flush_buffers(dctx->audioCodecContext); } } } void close_decoder(long decodec_id) { if (decodec_id > 0) { struct DecoderContext *ctx = (struct DecoderContext *) decodec_id; if (ctx->url) { free((void *) ctx->url); ctx->url = NULL; } if (ctx->avformatContext) { avformat_network_deinit(); avformat_flush(ctx->avformatContext); avformat_close_input(&(ctx->avformatContext)); avformat_free_context(ctx->avformatContext); ctx->avformatContext = NULL; } if (ctx->audioCodecContext) { avcodec_flush_buffers(ctx->audioCodecContext); avcodec_free_context(&(ctx->audioCodecContext)); avcodec_close(ctx->audioCodecContext); ctx->audioCodecContext = NULL; } if (ctx->videoCodecContext) { avcodec_flush_buffers(ctx->videoCodecContext); avcodec_free_context(&(ctx->videoCodecContext)); avcodec_close(ctx->videoCodecContext); ctx->videoCodecContext = NULL; } if (ctx->audio_queue) { AVFrame *frame = NULL; do { if (ctx->audio_queue->Size() > 0) { ctx->audio_queue->PopBack(frame); } av_frame_free(&frame); } while (frame != NULL); ctx->audio_queue->Close(); } if (ctx->video_queue) { AVFrame *frame = NULL; do { if (ctx->video_queue->Size() > 0) { ctx->video_queue->PopBack(frame); } av_frame_free(&frame); } while (frame != NULL); ctx->video_queue->Close(); } free(ctx); ctx = NULL; } } int av_read_decode_frame(long decodec_id) { auto *ctx = (DecoderContext *) decodec_id; pthread_create(&ctx->thread_id, 0, av_read_decode_thread, ctx); } void *av_read_decode_thread(void *pVoid) { int ret = 0; if (pVoid) { auto *ctx = (struct DecoderContext *) pVoid; if (ctx && ctx->avformatContext) { for (const auto &cur_item : ctx->vs_decodes) { //init ctx->decode_frame_ms = 0; if (cur_item->startTimeMs > 0) { ctx->start_time = cur_item->startTimeMs; seekToMs(ctx); } if (cur_item->endTimeMs > 0) ctx->end_time = cur_item->endTimeMs; while (1) { if (ctx->decode_frame_ms > ctx->end_time && ctx->end_time > 0) break; AVPacket *pkg = av_packet_alloc(); ret = av_read_frame(ctx->avformatContext, pkg); if (ret == AVERROR_EOF) { LOGE("startDecoder flush \n"); av_decode(ctx, NULL, AVMEDIA_TYPE_VIDEO); av_decode(ctx, NULL, AVMEDIA_TYPE_AUDIO); av_packet_free(&pkg); LOGE("av_read_frame eof!\n"); break; } enum AVMediaType type = ctx->avformatContext->streams[pkg->stream_index]->codecpar->codec_type; switch (type) { case AVMEDIA_TYPE_AUDIO: if (ctx->disableType == AUDIO) { av_packet_free(&pkg); continue; } break; case AVMEDIA_TYPE_VIDEO: if (ctx->disableType == VIDEO) { av_packet_free(&pkg); continue; } break; default: break; } //对 pts ,dts 校准,因为有偏移 if (pkg->dts != AV_NOPTS_VALUE && ctx->avformatContext->start_time > 0) pkg->dts += av_rescale_q(-ctx->avformatContext->start_time, AV_TIME_BASE_Q, ctx->avformatContext->streams[pkg->stream_index]->time_base); if (pkg->pts != AV_NOPTS_VALUE && ctx->avformatContext->start_time > 0) pkg->pts += av_rescale_q(-ctx->avformatContext->start_time, AV_TIME_BASE_Q, ctx->avformatContext->streams[pkg->stream_index]->time_base); if (ctx->force_Iframe && pkg->flags != AV_PKT_FLAG_KEY && ctx->avformatContext->streams[pkg->stream_index]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { av_packet_free(&pkg); continue; } else { ret = av_decode(ctx, pkg, type); if (ret == -4) { break; } } av_packet_free(&pkg); } } } ctx->audio_queue->PushBack(NULL); ctx->video_queue->PushBack(NULL); } pthread_exit(NULL); }