2013-06-29 22 views
3

我有一个程序,应该解复用输入mpeg-ts,将mpeg2转码为h264,然后将音频与转码后的视频一起复用。当我用VLC打开产生的多路复用文件时,我既没有音频也没有视频。这是相关的代码。MUX与libav

我的主要工作人员环路如下:

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代码,因为它是一个复杂得多,我可能会犯一些错误存在,因为我做的时基的对话等。然而声音我在做1:1拷贝。每个packet_t包都包含来自输​​入mpegts容器的av_read_frame的数据。在最糟糕的情况下,我希望我的音频工作,而不是我的视频。不过,我无法让其中的任何一个工作。看起来这些文档在做这类事情时相当模糊 - 我试过libav和ffmpeg irc频道都无济于事。任何有关我如何调试问题的信息将不胜感激。

+0

您使用MPEGTS输出格式。我不认为这是正确的,尝试一个容器,如MP4或AVI –

+0

AVI没有帮助,但与MP4我没有得到图片,但没有声音。我不明白的是为什么它会影响容器的性能? – LordDoskias

+0

我需要看到更多的程序。我可以提取音频(它是来自脏作业的剪辑)但是视频流有些问题。 – szatmary

回答

1

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

你必须把它放在容器之前重新调整时基。一般来说,修改多路复用器状态结构并不是你想要做的事情,我认为你在那里做了什么不会做你的想法。尝试打印出所有的时间基准,找出它们是什么。

每一帧必须至少重新计算PTS。如果您在调用编码器之前执行此操作,编码器将生成适当的DTS。对音频做相同的处理,但通常将DTS设置为AV_NO_PTS,有时您也可以将音频PTS设置为此。要轻松重新调整,请使用av_rescale(...)函数。

小心假设你在MPEG-TS容器具有MPEG-2数据,也并非总是如此。