当前位置: 首页 > 知识库问答 >
问题:

穆兴与利巴夫

阎安邦
2023-03-14

我有一个程序,它应该demux输入mpeg-ts,将mpeg2转码为h264,然后在转码视频旁边混音。当我用VLC打开结果的混音文件时,我既没有得到音频也没有得到视频。这是相关代码。

我的主要工作循环如下:

void
*writer_thread(void *thread_ctx) {

    struct transcoder_ctx_t *ctx = (struct transcoder_ctx_t *) thread_ctx;
    AVStream *video_stream = NULL, *audio_stream = NULL;
    AVFormatContext *output_context = init_output_context(ctx, &video_stream, &audio_stream);
    struct mux_state_t mux_state = {0};

    //from omxtx
    mux_state.pts_offset = av_rescale_q(ctx->input_context->start_time, AV_TIME_BASE_Q, output_context->streams[ctx->video_stream_index]->time_base);

    //write stream header if any
    avformat_write_header(output_context, NULL);

    //do not start doing anything until we get an encoded packet
    pthread_mutex_lock(&ctx->pipeline.video_encode.is_running_mutex);
    while (!ctx->pipeline.video_encode.is_running) {
        pthread_cond_wait(&ctx->pipeline.video_encode.is_running_cv, &ctx->pipeline.video_encode.is_running_mutex);
    }

    while (!ctx->pipeline.video_encode.eos || !ctx->processed_audio_queue->queue_finished) {
        //FIXME a memory barrier is required here so that we don't race 
        //on above variables 

        //fill a buffer with video data 
        OERR(OMX_FillThisBuffer(ctx->pipeline.video_encode.h, omx_get_next_output_buffer(&ctx->pipeline.video_encode)));

        write_audio_frame(output_context, audio_stream, ctx); //write full audio frame 
        //FIXME no guarantee that we have a full frame per packet?
        write_video_frame(output_context, video_stream, ctx, &mux_state); //write full video frame
        //encoded_video_queue is being filled by the previous command

    }

    av_write_trailer(output_context);

    //free all the resources
    avcodec_close(video_stream->codec);
    avcodec_close(audio_stream->codec);
    /* Free the streams. */
    for (int i = 0; i < output_context->nb_streams; i++) {
        av_freep(&output_context->streams[i]->codec);
        av_freep(&output_context->streams[i]);
    }

    if (!(output_context->oformat->flags & AVFMT_NOFILE)) {
        /* Close the output file. */
        avio_close(output_context->pb);
    }


    /* free the stream */
    av_free(output_context);
    free(mux_state.pps);
    free(mux_state.sps);
}

初始化libav输出上下文的代码如下:

static 
AVFormatContext *
init_output_context(const struct transcoder_ctx_t *ctx, AVStream **video_stream, AVStream **audio_stream) {
    AVFormatContext *oc;
    AVOutputFormat *fmt;
    AVStream *input_stream, *output_stream;
    AVCodec *c;
    AVCodecContext *cc;
    int audio_copied = 0; //copy just 1 stream

    fmt = av_guess_format("mpegts", NULL, NULL);
    if (!fmt) {
        fprintf(stderr, "[DEBUG] Error guessing format, dying\n");
        exit(199);
    }

    oc = avformat_alloc_context();
    if (!oc) {
        fprintf(stderr, "[DEBUG] Error allocating context, dying\n");
        exit(200);
    }

    oc->oformat = fmt;
    snprintf(oc->filename, sizeof(oc->filename), "%s", ctx->output_filename);
    oc->debug = 1;
    oc->start_time_realtime = ctx->input_context->start_time;
    oc->start_time = ctx->input_context->start_time;
    oc->duration = 0;
    oc->bit_rate = 0;

    for (int i = 0; i < ctx->input_context->nb_streams; i++) {
        input_stream = ctx->input_context->streams[i];
        output_stream = NULL;
        if (input_stream->index == ctx->video_stream_index) {
            //copy stuff from input video index
            c = avcodec_find_encoder(CODEC_ID_H264);
            output_stream = avformat_new_stream(oc, c);
            *video_stream = output_stream;
            cc = output_stream->codec;
            cc->width = input_stream->codec->width;
            cc->height = input_stream->codec->height;
            cc->codec_id = CODEC_ID_H264;
            cc->codec_type = AVMEDIA_TYPE_VIDEO;
            cc->bit_rate = ENCODED_BITRATE;
            cc->time_base = input_stream->codec->time_base;

            output_stream->avg_frame_rate = input_stream->avg_frame_rate;
            output_stream->r_frame_rate = input_stream->r_frame_rate;
            output_stream->start_time = AV_NOPTS_VALUE;

        } else if ((input_stream->codec->codec_type == AVMEDIA_TYPE_AUDIO) && !audio_copied)  { 
            /* i care only about audio */
            c = avcodec_find_encoder(input_stream->codec->codec_id);
            output_stream = avformat_new_stream(oc, c);
            *audio_stream = output_stream;
            avcodec_copy_context(output_stream->codec, input_stream->codec);
            /* Apparently fixes a crash on .mkvs with attachments: */
            av_dict_copy(&output_stream->metadata, input_stream->metadata, 0);
            /* Reset the codec tag so as not to cause problems with output format */
            output_stream->codec->codec_tag = 0;
            audio_copied = 1;
        }
    }

    for (int i = 0; i < oc->nb_streams; i++) {
        if (oc->oformat->flags & AVFMT_GLOBALHEADER)
            oc->streams[i]->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
        if (oc->streams[i]->codec->sample_rate == 0)
            oc->streams[i]->codec->sample_rate = 48000; /* ish */
    }

    if (!(fmt->flags & AVFMT_NOFILE)) {
        fprintf(stderr, "[DEBUG] AVFMT_NOFILE set, allocating output container\n");
        if (avio_open(&oc->pb, ctx->output_filename, AVIO_FLAG_WRITE) < 0) {
            fprintf(stderr, "[DEBUG] error creating the output context\n");
            exit(1);
        }
    }

    return oc;
}

最后这是编写音频的代码:

static
void
write_audio_frame(AVFormatContext *oc, AVStream *st, struct transcoder_ctx_t *ctx) {
    AVPacket pkt = {0}; // data and size must be 0;
    struct packet_t *source_audio;
    av_init_packet(&pkt);

    if (!(source_audio = packet_queue_get_next_item_asynch(ctx->processed_audio_queue))) {
        return;
    }

    pkt.stream_index = st->index;
    pkt.size = source_audio->data_length;
    pkt.data = source_audio->data;
    pkt.pts = source_audio->PTS;
    pkt.dts = source_audio->DTS;
    pkt.duration = source_audio->duration;
    pkt.destruct = avpacket_destruct;
    /* Write the compressed frame to the media file. */
    if (av_interleaved_write_frame(oc, &pkt) != 0) {
        fprintf(stderr, "[DEBUG] Error while writing audio frame\n");
    }

    packet_queue_free_packet(source_audio, 0);
}

可以从这里获得生成的mpeg4文件:http://87.120.131.41/dl/mpeg4.h264

我推荐了write_video_frame code,因为它要复杂得多,而且我在做音频的时基对话等时可能会出错,但我做的是1:1拷贝。每个数据包包含来自输入mpegts容器的av读取帧的数据。在最坏的情况下,我希望我的音频工作,而不是我的视频。然而,我无法让这两个都起作用。似乎文档中对制作这样的东西相当模糊——我尝试了libav和ffmpeg-irc频道,但都没有用。任何关于我如何调试该问题的信息都将不胜感激。

共有1个答案

孔飞舟
2023-03-14

当不同的容器在libav中产生不同的结果时,这几乎总是一个时基问题。所有容器都有他们喜欢的time_base,有些会接受自定义值......有时。

在将时基放入容器之前,您必须重新调整时基。通常,修补mux状态结构不是您想做的事情,我认为您在那里所做的并没有按照您的想法进行。尝试打印出所有的时基以找出它们是什么。

每一帧都必须至少重新计算PTS。如果在调用encode之前执行此操作,编码器将生成正确的DTS。对音频也要这样做,但通常会将DTS设置为AV_NO_PTS,有时也可以将音频设置为AV_NO_PTS。要轻松重缩放,请使用av_重缩放(…)功能。

请小心假设您在MPEG-TS容器中有MPEG-2数据,但这并不总是正确的。

 类似资料:
  • 双非本科,参加了无数场面试,以下是最近面阿里的总结(来源于网络): 阿里 面经1 简单介绍项目 知道哪些数据结构以及他们的特点 链表增删快,那如何提高其查询效率,有没有什么想法? B+树了解吗?B+树如何范围查询?B+树退化的极端情况是什么? 跳表了解吗? 大顶堆、小顶堆了解吗? 实现长地址请求到服务端,然后服务端重定向短地址给客户端,如何实现长短地址的互相映射? 那我现在有10份数据,有1000

  • #产品2023笔面经# 去年投简历直接挂 今年投终于收到了笔试 北森题库 40min 10言语理解 10资料分析 10图形推理 51性格测试 形式上和携程、shein的一致,只是性格测评不太一样,求一个面试机会吧

  • 这次是被调岗加面一轮,由于自己的身体状况原因,中间歇了一段时间没有面试,发现距离上次面试已经过去大半个月了。 1.自我介绍 2.介绍一下上段实习主要做什么的,并且进行深挖 3.介绍一下上上段实习是干嘛的,通过什么模型之类的进行工作 4.你觉得你碰到最有挑战的工作是什么 5.为什么投了阿里 6.还投了什么其他公司,并追问为什么没去呢 我:因为没过面试 7.那你觉得面试为什么会没通过 8.读书和工作期

  • 饿了么的数据研发岗位,整体面试体验很好!记录一下我的面(凉)经和反思吧~ 8-9 投递 8-13 行测+性格 8-21 笔试,15选择题,3个编程,我分别是100,90,6 8-25 一面 面试官先自我介绍,然后说大概全程30分钟,然后让我自我介绍。接着问更倾向于做算法还是做开发。有没有大数据,数据开发相关的经验,或者课。 接着问sql常用语句,优化方法。 数据结构相关,排序算法。 手撕代码:两个

  • 双非本科,参加了无数场面试,以下是最近面阿里的总结: 阿里 面经1 简单介绍项目 知道哪些数据结构以及他们的特点 链表增删快,那如何提高其查询效率,有没有什么想法? B+树了解吗?B+树如何范围查询?B+树退化的极端情况是什么? 跳表了解吗? 大顶堆、小顶堆了解吗? 实现长地址请求到服务端,然后服务端重定向短地址给客户端,如何实现长短地址的互相映射? 那我现在有10份数据,有1000个线程来争抢,

  • 然后我尝试这样写音频帧: 问题是我从来不传递这个语句:“if(pkt.stream_index==st->index)”。数据包流索引从来不等于音频流索引。谁能指出我错在哪里? 更新: 顺便说一句,下面的答案没有帮助,因为它假设音频和视频流都来自同一个文件,而在我的情况下,只有音频来自外部源。