2016-11-24 11 views
1

avcodec_decode_video2H.264トランスポートストリームのavcodec_decode_video2に渡す内容

The documentationに渡すために、私はMPEG-2トランスポートストリームパケットのコレクションからH.26​​4ビデオをデコードしたいが、私は明確ではないですが合格すると言い、「入力バッファを含む入力AVPacketを。」

しかし、入力バッファには何が必要ですか?

PESパケットは、複数のTSパケットのペイロード部分に分散され、NALUはPES内に配置されます。 TSフラグメントを渡しますか? PES全体? PESペイロードのみ?

このSample Codeは言及:

しかし、いくつかの他のコーデック(msmpeg4、MPEG4)がベースと本質的にフレームなので、 あなたは正確に一つのフレームのすべてのデータとそれらを呼び出す必要があります。 も初期化する前に 'width'と 'height'を初期化する必要があります。 TSパケットのペイロードのフラグメントを渡す

しかし、私は「すべてのデータが」何を意味するかには何の情報を見つけることができません...

が動作していません。

AVPacket avDecPkt; 
av_init_packet(&avDecPkt); 
avDecPkt.data = inbuf_ptr; 
avDecPkt.size = esBufSize; 

len = avcodec_decode_video2(mpDecoderContext, mpFrameDec, &got_picture, &avDecPkt); 
if (len < 0) 
{ 
    printf(" TS PKT #%.0f. Error decoding frame #%04d [rc=%d '%s']\n", 
     tsPacket.pktNum, mDecodedFrameNum, len, av_make_error_string(errMsg, 128, len)); 
    return; 
} 

出力

[h264 @ 0x81cd2a0] no frame! 
TS PKT #2973. Error decoding frame #0001 [rc=-1094995529 'Invalid data found when processing input'] 

EDIT

WLGfxの優れたヒットを使用して、私はこの簡単なプログラムを使ってTSパケットを解読しようとしました。入力として、私は、ビデオPIDからの個のTSパケットだけを含むファイルを用意しました。

近づいていますが、FormatContextの設定方法はわかりません。以下のコードは、av_read_frame()(および内部ではret = s->iformat->read_packet(s, pkt))のsegfaultsです。 s-> iformatはゼロです。

提案?

EDIT II - 申し訳ありませんが、ためになった後のソースコード** ** EDIT III -

/* 
* Test program for video decoder 
*/ 

#include <stdio.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 

extern "C" { 

#ifdef __cplusplus 
    #define __STDC_CONSTANT_MACROS 
    #ifdef _STDINT_H 
     #undef _STDINT_H 
    #endif 
    #include <stdint.h> 
#endif 
} 

extern "C" { 
#include "libavcodec/avcodec.h" 
#include "libavformat/avformat.h" 
#include "libswscale/swscale.h" 
#include "libavutil/imgutils.h" 
#include "libavutil/opt.h" 
} 


class VideoDecoder 
{ 
public: 
    VideoDecoder(); 
    bool rcvTsPacket(AVPacket &inTsPacket); 

private: 
    AVCodec   *mpDecoder; 
    AVCodecContext *mpDecoderContext; 
    AVFrame   *mpDecodedFrame; 
    AVFormatContext *mpFmtContext; 

}; 

VideoDecoder::VideoDecoder() 
{ 
    av_register_all(); 

    // FORMAT CONTEXT SETUP 
    mpFmtContext = avformat_alloc_context(); 
    mpFmtContext->flags = AVFMT_NOFILE; 
    // ????? WHAT ELSE ???? // 

    // DECODER SETUP 
    mpDecoder = avcodec_find_decoder(AV_CODEC_ID_H264); 
    if (!mpDecoder) 
    { 
     printf("Could not load decoder\n"); 
     exit(11); 
    } 

    mpDecoderContext = avcodec_alloc_context3(NULL); 
    if (avcodec_open2(mpDecoderContext, mpDecoder, NULL) < 0) 
    { 
     printf("Cannot open decoder context\n"); 
     exit(1); 
    } 

    mpDecodedFrame = av_frame_alloc(); 
} 

bool 
VideoDecoder::rcvTsPacket(AVPacket &inTsPkt) 
{ 
    bool ret = true; 

    if ((av_read_frame(mpFmtContext, &inTsPkt)) < 0) 
    { 
     printf("Error in av_read_frame()\n"); 
     ret = false; 
    } 
    else 
    { 
     // success. Decode the TS packet 
     int got; 
     int len = avcodec_decode_video2(mpDecoderContext, mpDecodedFrame, &got, &inTsPkt); 
     if (len < 0) 
      ret = false; 

     if (got) 
      printf("GOT A DECODED FRAME\n"); 
    } 

    return ret; 
} 

int 
main(int argc, char **argv) 
{ 
    if (argc != 2) 
    { 
     printf("Usage: %s tsInFile\n", argv[0]); 
     exit(1); 
    } 

    FILE *tsInFile = fopen(argv[1], "r"); 
    if (!tsInFile) 
    { 
     perror("Could not open TS input file"); 
     exit(2); 
    } 

    unsigned int tsPktNum = 0; 
    uint8_t  tsBuffer[256]; 
    AVPacket  tsPkt; 
    av_init_packet(&tsPkt); 

    VideoDecoder vDecoder; 

    while (!feof(tsInFile)) 
    { 
     tsPktNum++; 

     tsPkt.size = 188; 
     tsPkt.data = tsBuffer; 
     fread(tsPkt.data, 188, 1, tsInFile); 

     vDecoder.rcvTsPacket(tsPkt); 
    } 
} 
+0

受信パケットには、オーディオ、ビデオ、字幕、およびデータ用のストリームIDがあります。ビデオストリームのコーデックコンテキストを決定して作成したら、自分のデコード機能にパケットを渡すだけです。最高の情報源は、ソースコードを表示することです... – WLGfx

+0

ありがとうございます。 TSパケットは、ビデオのみを含む単一のPIDに既に制約されている。それはH.264なので、AV_CODEC_ID_H264をデコーダとして使用しました。あなたが "パケットを渡す"と言うとき、どちらのパケット?完全なTSパケットまたは再組み立てされたPESパケット?私はffplayをチェックアウトします。 – Danny

答えて

0

あなたが最初に取得するavcodecライブラリを使用する必要がありますTS PKTキューを読んでシミュレートするために更新サンプルコード圧縮されたフレームをファイルから取り出します。次に、avcodec_decode_video2を使用してそれらをデコードすることができます。このチュートリアルをご覧くださいhttp://dranger.com/ffmpeg/

+0

ありがとうございますが、ファイルがありません。私はMPEG-2トランスポートストリームパケットのコレクションを持っています... – Danny

+0

いいえ問題ファイルの代わりにメモリから読み込むには、異なるioコンテキストを使用してください。 – szatmary

+0

TSパケットは時間の経過と共に1つずつ到着する。だから、TSパケットのコレクションからPESパケットを抽出し、再構築されたPESパケットを取得したらavcodec_decode_video2を呼び出す必要がありますか?または、PESヘッダーを削除して、開始コードからESを渡しますか?ありがとう! – Danny

1

私はMPEG-TSで作業していたため、いくつかのコードスニペットがあります。ストリームIDの私はすでに発見され、コーデックのコンテキストを持っているに対して、各パケットをチェックし、私のパケットのスレッドで開始

:オーディオ、ビデオ、および字幕の

void *FFMPEG::thread_packet_function(void *arg) { 
    FFMPEG *ffmpeg = (FFMPEG*)arg; 
    for (int c = 0; c < MAX_PACKETS; c++) 
     ffmpeg->free_packets[c] = &ffmpeg->packet_list[c]; 
    ffmpeg->packet_pos = MAX_PACKETS; 

    Audio.start_decoding(); 
    Video.start_decoding(); 
    Subtitle.start_decoding(); 

    while (!ffmpeg->thread_quit) { 
     if (ffmpeg->packet_pos != 0 && 
       Audio.okay_add_packet() && 
       Video.okay_add_packet() && 
       Subtitle.okay_add_packet()) { 

      pthread_mutex_lock(&ffmpeg->packet_mutex); // get free packet 
      AVPacket *pkt = ffmpeg->free_packets[--ffmpeg->packet_pos]; // pre decrement 
      pthread_mutex_unlock(&ffmpeg->packet_mutex); 

      if ((av_read_frame(ffmpeg->fContext, pkt)) >= 0) { // success 
       int id = pkt->stream_index; 
       if (id == ffmpeg->aud_stream.stream_id) Audio.add_packet(pkt); 
       else if (id == ffmpeg->vid_stream.stream_id) Video.add_packet(pkt); 
       else if (id == ffmpeg->sub_stream.stream_id) Subtitle.add_packet(pkt); 
       else { // unknown packet 
        av_packet_unref(pkt); 

        pthread_mutex_lock(&ffmpeg->packet_mutex); // put packet back 
        ffmpeg->free_packets[ffmpeg->packet_pos++] = pkt; 
        pthread_mutex_unlock(&ffmpeg->packet_mutex); 

        //LOGI("Dumping unknown packet, id %d", id); 
       } 
      } else { 
       av_packet_unref(pkt); 

       pthread_mutex_lock(&ffmpeg->packet_mutex); // put packet back 
       ffmpeg->free_packets[ffmpeg->packet_pos++] = pkt; 
       pthread_mutex_unlock(&ffmpeg->packet_mutex); 

       //LOGI("No packet read"); 
      } 
     } else { // buffers full so yield 
      //LOGI("Packet reader on hold: Audio-%d, Video-%d, Subtitle-%d", 
      // Audio.packet_pos, Video.packet_pos, Subtitle.packet_pos); 
      usleep(1000); 
      //sched_yield(); 
     } 
    } 
    return 0; 
} 

各デコーダが受信独自のスレッドを持っていますリングバッファ内の上記スレッドからのパケット。私はデインターを自分のスレッドに分けなければなりませんでした。なぜなら、デインタレースフィルターを使い始めたときにCPU使用量が増えていたからです。

私のビデオデコーダはバッファからパケットを読み込み、それが終わったらパケットをunref'dに戻して、再び使用することができます。パケットバッファのバランシングは、すべてが実行されてからあまり時間がかかりません。

は、ここに私のビデオデコーダから切り取らです:あなたが見ることができるように前後にパケットを渡すとき、私はミューテックスを使用してい

void *VideoManager::decoder(void *arg) { 
    LOGI("Video decoder started"); 
    VideoManager *mgr = (VideoManager *)arg; 
    while (!ffmpeg.thread_quit) { 
     pthread_mutex_lock(&mgr->packet_mutex); 
     if (mgr->packet_pos != 0) { 
      // fetch first packet to decode 
      AVPacket *pkt = mgr->packets[0]; 

      // shift list down one 
      for (int c = 1; c < mgr->packet_pos; c++) { 
       mgr->packets[c-1] = mgr->packets[c]; 
      } 
      mgr->packet_pos--; 
      pthread_mutex_unlock(&mgr->packet_mutex); // finished with packets array 

      int got; 
      AVFrame *frame = ffmpeg.vid_stream.frame; 
      avcodec_decode_video2(ffmpeg.vid_stream.context, frame, &got, pkt); 
      ffmpeg.finished_with_packet(pkt); 
      if (got) { 
#ifdef INTERLACE_ALL 
       if (!frame->interlaced_frame) mgr->add_av_frame(frame, 0); 
       else { 
        if (!mgr->filter_initialised) mgr->init_filter_graph(frame); 
        av_buffersrc_add_frame_flags(mgr->filter_src_ctx, frame, AV_BUFFERSRC_FLAG_KEEP_REF); 
        int c = 0; 
        while (true) { 
         AVFrame *filter_frame = ffmpeg.vid_stream.filter_frame; 
         int result = av_buffersink_get_frame(mgr->filter_sink_ctx, filter_frame); 
         if (result == AVERROR(EAGAIN) || 
           result == AVERROR(AVERROR_EOF) || 
           result < 0) 
          break; 
         mgr->add_av_frame(filter_frame, c++); 
         av_frame_unref(filter_frame); 
        } 
        //LOGI("Interlaced %d frames, decode %d, playback %d", c, mgr->decode_pos, mgr->playback_pos); 
       } 
#elif defined(INTERLACE_HALF) 
       if (!frame->interlaced_frame) mgr->add_av_frame(frame, 0); 
       else { 
        if (!mgr->filter_initialised) mgr->init_filter_graph(frame); 
        av_buffersrc_add_frame_flags(mgr->filter_src_ctx, frame, AV_BUFFERSRC_FLAG_KEEP_REF); 
        int c = 0; 
        while (true) { 
         AVFrame *filter_frame = ffmpeg.vid_stream.filter_frame; 
         int result = av_buffersink_get_frame(mgr->filter_sink_ctx, filter_frame); 
         if (result == AVERROR(EAGAIN) || 
           result == AVERROR(AVERROR_EOF) || 
           result < 0) 
          break; 
         mgr->add_av_frame(filter_frame, c++); 
         av_frame_unref(filter_frame); 
        } 
        //LOGI("Interlaced %d frames, decode %d, playback %d", c, mgr->decode_pos, mgr->playback_pos); 
       } 
#else 
       mgr->add_av_frame(frame, 0); 
#endif 
      } 
      //LOGI("decoded video packet"); 
     } else { 
      pthread_mutex_unlock(&mgr->packet_mutex); 
     } 
    } 
    LOGI("Video decoder ended"); 
} 

フレームが取得されたら、後で別のバッファリストに使用するためにYUVバッファをフレームからコピーするだけです。私はYUVを変換しません、私はGPU上のYUVをRGBに変換するシェーダーを使用します。

次のスニペットは、自分のデコードされたフレームをバッファリストに追加します。これはデータの取り扱い方法を理解するのに役立ちます。

void VideoManager::add_av_frame(AVFrame *frame, int field_num) { 
    int y_linesize = frame->linesize[0]; 
    int u_linesize = frame->linesize[1]; 

    int hgt = frame->height; 

    int y_buffsize = y_linesize * hgt; 
    int u_buffsize = u_linesize * hgt/2; 

    int buffsize = y_buffsize + u_buffsize + u_buffsize; 

    VideoBuffer *buffer = &buffers[decode_pos]; 

    if (ffmpeg.is_network && playback_pos == decode_pos) { // patched 25/10/16 wlgfx 
     buffer->used = false; 
     if (!buffer->data) buffer->data = (char*)mem.alloc(buffsize); 
     if (!buffer->data) { 
      LOGI("Dropped frame, allocation error"); 
      return; 
     } 
    } else if (playback_pos == decode_pos) { 
     LOGI("Dropped frame, ran out of decoder frame buffers"); 
     return; 
    } else if (!buffer->data) { 
     buffer->data = (char*)mem.alloc(buffsize); 
     if (!buffer->data) { 
      LOGI("Dropped frame, allocation error."); 
      return; 
     } 
    } 

    buffer->y_frame = buffer->data; 
    buffer->u_frame = buffer->y_frame + y_buffsize; 
    buffer->v_frame = buffer->y_frame + y_buffsize + u_buffsize; 

    buffer->wid = frame->width; 
    buffer->hgt = hgt; 

    buffer->y_linesize = y_linesize; 
    buffer->u_linesize = u_linesize; 

    int64_t pts = av_frame_get_best_effort_timestamp(frame); 
    buffer->pts = pts; 
    buffer->buffer_size = buffsize; 

    double field_add = av_q2d(ffmpeg.vid_stream.context->time_base) * field_num; 
    buffer->frame_time = av_q2d(ts_stream) * pts + field_add; 

    memcpy(buffer->y_frame, frame->data[0], (size_t) (buffer->y_linesize * buffer->hgt)); 
    memcpy(buffer->u_frame, frame->data[1], (size_t) (buffer->u_linesize * buffer->hgt/2)); 
    memcpy(buffer->v_frame, frame->data[2], (size_t) (buffer->u_linesize * buffer->hgt/2)); 

    buffer->used = true; 
    decode_pos = (++decode_pos) % MAX_VID_BUFFERS; 

    //if (field_num == 0) LOGI("Video %.2f, %d - %d", 
    //  buffer->frame_time - Audio.pts_start_time, decode_pos, playback_pos); 
} 

私が助けることができる他のものがあるなら、私に叫びを与えてください。 :-)

EDIT:

私はそれがH264、MPEG2、または別であるかどうか、自動的にコーデックを決定し、私のビデオストリームコンテキストを開く方法スニペット:

void FFMPEG::open_video_stream() { 
    vid_stream.stream_id = av_find_best_stream(fContext, AVMEDIA_TYPE_VIDEO, 
               -1, -1, &vid_stream.codec, 0); 
    if (vid_stream.stream_id == -1) return; 

    vid_stream.context = fContext->streams[vid_stream.stream_id]->codec; 

    if (!vid_stream.codec || avcodec_open2(vid_stream.context, 
      vid_stream.codec, NULL) < 0) { 
     vid_stream.stream_id = -1; 
     return; 
    } 

    vid_stream.frame = av_frame_alloc(); 
    vid_stream.filter_frame = av_frame_alloc(); 
} 

EDIT2:

これは、入力ストリームがファイルかURLかを問わず開いた方法です。 AVFormatContextはストリームの主なコンテキストです。

bool FFMPEG::start_stream(char *url_, float xtrim, float ytrim, int gain) { 
    aud_stream.stream_id = -1; 
    vid_stream.stream_id = -1; 
    sub_stream.stream_id = -1; 

    this->url = url_; 
    this->xtrim = xtrim; 
    this->ytrim = ytrim; 
    Audio.volume = gain; 

    Audio.init(); 
    Video.init(); 

    fContext = avformat_alloc_context(); 

    if ((avformat_open_input(&fContext, url_, NULL, NULL)) != 0) { 
     stop_stream(); 
     return false; 
    } 

    if ((avformat_find_stream_info(fContext, NULL)) < 0) { 
     stop_stream(); 
     return false; 
    } 

    // network stream will overwrite packets if buffer is full 

    is_network = url.substr(0, 4) == "udp:" || 
        url.substr(0, 4) == "rtp:" || 
        url.substr(0, 5) == "rtsp:" || 
        url.substr(0, 5) == "http:"; // added for wifi broadcasting ability 

    // determine if stream is audio only 

    is_mp3 = url.substr(url.size() - 4) == ".mp3"; 

    LOGI("Stream: %s", url_); 

    if (!open_audio_stream()) { 
     stop_stream(); 
     return false; 
    } 

    if (is_mp3) { 
     vid_stream.stream_id = -1; 
     sub_stream.stream_id = -1; 
    } else { 
     open_video_stream(); 
     open_subtitle_stream(); 

     if (vid_stream.stream_id == -1) { // switch to audio only 
      close_subtitle_stream(); 
      is_mp3 = true; 
     } 
    } 

    LOGI("Audio: %d, Video: %d, Subtitle: %d", 
      aud_stream.stream_id, 
      vid_stream.stream_id, 
      sub_stream.stream_id); 

    if (aud_stream.stream_id != -1) { 
     LOGD("Audio stream time_base {%d, %d}", 
      aud_stream.context->time_base.num, 
      aud_stream.context->time_base.den); 
    } 

    if (vid_stream.stream_id != -1) { 
     LOGD("Video stream time_base {%d, %d}", 
      vid_stream.context->time_base.num, 
      vid_stream.context->time_base.den); 
    } 

    LOGI("Starting packet and decode threads"); 

    thread_quit = false; 

    pthread_create(&thread_packet, NULL, &FFMPEG::thread_packet_function, this); 

    Display.set_overlay_timout(3.0); 

    return true; 
} 

EDIT:(AVPacketを構築する)

デコーダに送信するAVPacketを構築...

AVPacket packet; 
av_init_packet(&packet); 
packet.data = myTSpacketdata; // pointer to the TS packet 
packet.size = 188; 

あなたがパケットを再利用することができるはずです。そして、それは非審判を必要とするかもしれません。

+0

うわー、それは素晴らしいですね。ありがとう。確認するために、あなたの "packet_list"と "packets"はMPEG-TSパケットですか? 0x47同期バイト、PID、TSヘッダーなどでは? – Danny

+0

はい、av_read_frame()から受信したパケットです。シンクバイトをチェックする必要はなく、パケットスレッドと同様にストリームIDだけをチェックする必要はありません。 – WLGfx

+0

NB:ffmpeg 3.1.4と3.2の違いに気付きました。初期のバージョンでは、デコーダに対して1〜1パケットが与えられました。 3。2は、ffmpegがavcodec_decode_video2()関数を2つの別々の呼び出しにデカップリングして非難したため、パケットが小さくなりました。いずれにしても、&変数をチェックするだけでこれは違いはありません。 – WLGfx

関連する問題