hrdzkj 2019-11-14 15:06 采纳率: 0%
浏览 1950

ffmpeg编解码异常,Resource temporarily unavailable

修改ffplay官方代码,使用ffmpeg实现从新编码保存mp4功能。调用avcodec_receive_packet返回-1,提示Resource temporarily unavailable,保存出来的文件没有I帧。

完整代码:

https://github.com/hrdzkj/ffplay/tree/ffplayOpengl

(注意是在ffplayOpengl分支)

关键代码:

int ffp_start_record(FFPlayer *ffp, const char *file_name)
{
assert(ffp);
VideoState *is = ffp->is;
if (!file_name || !strlen(file_name)) { // 没有路径
av_log(ffp, AV_LOG_ERROR, "filename is invalid");
goto end;
}

if (!is || !is->ic || is->paused || is->abort_request) { // 没有上下文,或者上下文已经停止
    av_log(ffp, AV_LOG_ERROR, "is,is->ic,is->paused is invalid");
    goto end;
}

if (ffp->is_record) { // 已经在录制
    av_log(ffp, AV_LOG_ERROR, "recording has started");
    goto end;
}

ffp->m_ofmt_ctx = NULL;
ffp->is_record = 0;
ffp->record_error = 0;

// todo avformat_new_stream,avcodec_copy_context/avcodec_parameters_from_context 等api函数的作用,初始化了什么东西
// 初始化一个用于输出的AVFormatContext结构体
avformat_alloc_output_context2(&ffp->m_ofmt_ctx, NULL, "mp4", file_name);
if (!ffp->m_ofmt_ctx) {
    av_log(ffp, AV_LOG_ERROR, "Could not create output context filename is %s\n", file_name);
    goto end;
}

stream_ctx = av_mallocz_array(is->ic->nb_streams, sizeof(*stream_ctx));
if (!stream_ctx) {
    goto end;
}


for (int i = 0; i < is->ic->nb_streams; i++) {
    int ret;
    AVCodec *encoder;
    AVCodecContext *enc_ctx;
    AVCodecContext *dec_ctx = is->ic->streams[i]->codec;
    if (dec_ctx->codec_type != AVMEDIA_TYPE_VIDEO && dec_ctx->codec_type != AVMEDIA_TYPE_AUDIO) {
        continue;
    }

    //对照输入流创建输出流通道
    AVStream *in_stream = is->ic->streams[i];
    AVStream *out_stream = avformat_new_stream(ffp->m_ofmt_ctx, NULL);
    if (!out_stream) {
        av_log(ffp, AV_LOG_ERROR, "Failed allocating output stream\n");
        goto end;
    }

    //查找编码器、创建编码器上下文、设置编码器参数,然后打开编码器
    encoder = avcodec_find_encoder(dec_ctx->codec_id); 
    if (!encoder) {
        av_log(NULL, AV_LOG_FATAL, "Necessary encoder not found\n");
        goto end;
    }
    enc_ctx = avcodec_alloc_context3(encoder);
    if (!enc_ctx) {
        av_log(NULL, AV_LOG_FATAL, "Failed to allocate the encoder context\n");
        goto end;
    }

    //不是帧率的问题,是I P B帧的区别导致的问题了
    ret = avcodec_parameters_to_context(enc_ctx, in_stream->codecpar);
    if (ret < 0) {
        printf("Failed to copy context input to output stream codec context\n");
        goto end;
    }

    if (enc_ctx->codec_type == AVMEDIA_TYPE_VIDEO) {
        //enc_ctx->height = dec_ctx->height;
        //enc_ctx->width = dec_ctx->width;
        //enc_ctx->sample_aspect_ratio = dec_ctx->sample_aspect_ratio;
        //enc_ctx->gop_size = 10;
        //enc_ctx->max_b_frames = 0;

        //if (encoder->pix_fmts)
            //enc_ctx->pix_fmt = encoder->pix_fmts[0];
        //else
        //  enc_ctx->pix_fmt = dec_ctx->pix_fmt;

        enc_ctx->time_base = av_inv_q(dec_ctx->framerate);

        //enc_ctx->time_base = //(AVRational) { 1, 30 };
        //enc_ctx->framerate = //(AVRational) { 30, 1 };

    }
    else {
        //enc_ctx->sample_rate = dec_ctx->sample_rate;
        //enc_ctx->channel_layout = dec_ctx->channel_layout;
        //enc_ctx->channels = av_get_channel_layout_nb_channels(enc_ctx->channel_layout);
        //enc_ctx->sample_fmt = encoder->sample_fmts[0];
        enc_ctx->time_base = (AVRational) { 1, dec_ctx->sample_rate };
    }


    if (ffp->m_ofmt_ctx->flags & AVFMT_GLOBALHEADER) {
        enc_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
    }

    ret = avcodec_open2(enc_ctx, encoder, NULL);
    if (ret < 0) {
        av_log(NULL, AV_LOG_ERROR, "Cannot open video encoder for stream #%u\n", i);
        goto end;
    }

    ret = avcodec_copy_context(out_stream->codec, in_stream->codec);
     //ret = avcodec_parameters_to_context(out_stream->codec, in_stream->codecpar);
    //ret = avcodec_parameters_from_context(out_stream->codecpar, enc_ctx);
    if (ret < 0) {
        av_log(NULL, AV_LOG_ERROR, "Failed to copy encoder parameters to output stream #%u\n", i);
        goto end;
    }

    out_stream->time_base = enc_ctx->time_base;
    stream_ctx[i].enc_ctx = enc_ctx;
}



av_dump_format(ffp->m_ofmt_ctx, 0, file_name, 1);

// 打开输出文件
if (!(ffp->m_ofmt_ctx->oformat->flags & AVFMT_NOFILE)) {
    if (avio_open(&ffp->m_ofmt_ctx->pb, file_name, AVIO_FLAG_WRITE) < 0) {
        av_log(ffp, AV_LOG_ERROR, "Could not open output file '%s'", file_name);
        goto end;
    }
}

// 写视频文件头
if (avformat_write_header(ffp->m_ofmt_ctx, NULL) < 0) {
    av_log(ffp, AV_LOG_ERROR, "Error occurred when opening output file\n");
    goto end;
}


ffp->is_first = 0;
ffp->is_record = 1;
ffp->record_error = 0;

return 0;

end:
ffp->record_error = 1;
return -1;
}

static void encode(FFPlayer *ffp,AVCodecContext *enc_ctx, AVFrame *frame)
{
int ret;
int got_frame;// malloc(sizeof(int));
AVPacket pkt = { 0 };
av_init_packet(&pkt);
pkt.data = NULL;
pkt.size = 0;

int stream_index=getMediaTypeStreamIndex(ffp,AVMEDIA_TYPE_VIDEO);
/* send the frame to the encoder */
if (frame)
    printf("Send frame %3"PRId64"\n", frame->pts);

/*
//旧的api
ret= avcodec_encode_video2(enc_ctx, &pkt,frame, &got_frame);
if (ret < 0)
return ret;
if (!(got_frame)) {
fprintf(stderr, "avcodec_encode_video2 fail \n");
return ;
}

ffp_record_file(ffp, &pkt);
av_packet_unref(&pkt);
*/

//新的api
ret = avcodec_send_frame(enc_ctx, frame);
if (ret < 0) {
fprintf(stderr, "Error sending a frame for encoding\n");
return -1;
}

while (1) {
    ret = avcodec_receive_packet(enc_ctx, &pkt);
    if (ret) {
        fprintf(stderr, "Error encoding ret= %d,%s \n", ret,av_err2str(ret));
        break;
    }

    fprintf(stdout, " -----> encoding success!\n");
    pkt.stream_index = stream_index;
    ffp_record_file(ffp,&pkt);
    av_packet_unref(&pkt);
}

}

提示Resource temporarily unavailable的截图

图片说明

生成的mp4文件无I帧截图

图片说明

  • 写回答

0条回答

    报告相同问题?

    悬赏问题

    • ¥15 oracle集群安装出bug
    • ¥15 关于#python#的问题:自动化测试
    • ¥20 问题请教!vue项目关于Nginx配置nonce安全策略的问题
    • ¥15 教务系统账号被盗号如何追溯设备
    • ¥20 delta降尺度方法,未来数据怎么降尺度
    • ¥15 c# 使用NPOI快速将datatable数据导入excel中指定sheet,要求快速高效
    • ¥15 再不同版本的系统上,TCP传输速度不一致
    • ¥15 高德地图点聚合中Marker的位置无法实时更新
    • ¥15 DIFY API Endpoint 问题。
    • ¥20 sub地址DHCP问题