2013-06-29 28 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數據,也並非總是如此。