123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417 |
- //
- // 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<AVFrame *>(dctx->fps);
- dctx->video_queue = new BlockQueue<AVFrame *>(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);
- }
|