2015-05-28 3 views
2

Я пытаюсь использовать C++ API FFMpeg (версия 20150526) под Windows, используя предварительно созданные двоичные файлы для декодирования видеофайла h264 (* .ts).FFmpeg C++ api decode h264 error

Я написал очень простой код, который автоматически обнаруживает требуемый кодек из самого файла (и это AV_CODEC_ID_H264, как и ожидалось).

Затем я снова открываю видеофайл в режиме чтения-двоичного кода, и я читаю из него буфер фиксированного размера байтов и предоставляю чтение байтов декодеру в течение цикла while до конца файла. Тем не менее, когда я вызвать функцию avcodec_decode_video2 большое количество ошибок произойдет, как следующие из них:

[H264 @ 008df020] верхний блок недоступен для режима запрашиваемого интро в 34 0

[H264 @ 008df020] ошибка при декодировании MB 34 0, потоковый 3152

[h264 @ 008df020] decode_slice_header ошибка

Иногда функция avcodec_decode_video2 устанавливает значение got_picture_ptr 1 и, следовательно, я ожидаю, чтобы найти хороший кадр. Вместо этого, хотя все вычисления успешны, когда я просматриваю декодированный кадр (используя OpenCV только для целей визуализации), я вижу серый с некоторыми артефактами.

Если я использую тот же код для декодирования файла * .avi, он отлично работает.

Чтение примеров FFMpeg Я не нашел решение моей проблемы. Я также внедрил решение, предложенное в simlar-вопросе FFmpeg c++ H264 decoding error, но это не сработало.

Кто-нибудь знает, где ошибка?

Заранее благодарим за любой ответ!

Код состоит в следующем [EDIT: код обновляется, включая управление парсера]:

#include <iostream> 
#include <iomanip> 
#include <string> 
#include <sstream> 

#include <opencv2/opencv.hpp> 

#ifdef __cplusplus 
extern "C" 
{ 
#endif // __cplusplus 
#include <libavcodec/avcodec.h> 
#include <libavdevice/avdevice.h> 
#include <libavfilter/avfilter.h> 
#include <libavformat/avformat.h> 
#include <libavformat/avio.h> 
#include <libavutil/avutil.h> 
#include <libpostproc/postprocess.h> 
#include <libswresample/swresample.h> 
#include <libswscale/swscale.h> 
#ifdef __cplusplus 
} // end extern "C". 
#endif // __cplusplus 

#define INBUF_SIZE 4096 

void main() 
{ 
    AVCodec*   l_pCodec; 
    AVCodecContext*  l_pAVCodecContext; 
    SwsContext*   l_pSWSContext; 
    AVFormatContext* l_pAVFormatContext; 
    AVFrame*   l_pAVFrame; 
    AVFrame*   l_pAVFrameBGR; 
    AVPacket   l_AVPacket; 
    AVPacket   l_AVPacket_out; 
    AVStream*   l_pStream; 
    AVCodecParserContext* l_pParser; 
    FILE*    l_pFile_in; 
    FILE*    l_pFile_out; 
    std::string   l_sFile; 
    int     l_iResult; 
    int     l_iFrameCount; 
    int     l_iGotFrame; 
    int     l_iBufLength; 
    int     l_iParsedBytes; 
    int     l_iPts; 
    int     l_iDts; 
    int     l_iPos; 
    int     l_iSize; 
    int     l_iDecodedBytes; 
    uint8_t    l_auiInBuf[INBUF_SIZE + FF_INPUT_BUFFER_PADDING_SIZE]; 
    uint8_t*   l_pData; 
    cv::Mat    l_cvmImage; 

    l_pCodec = NULL; 
    l_pAVCodecContext = NULL; 
    l_pSWSContext = NULL; 
    l_pAVFormatContext = NULL; 
    l_pAVFrame = NULL; 
    l_pAVFrameBGR = NULL; 
    l_pParser = NULL; 
    l_pStream = NULL; 
    l_pFile_in = NULL; 
    l_pFile_out = NULL; 
    l_iPts = 0; 
    l_iDts = 0; 
    l_iPos = 0; 
    l_pData = NULL; 

    l_sFile = "myvideo.ts"; 

    avdevice_register_all(); 
    avfilter_register_all(); 
    avcodec_register_all(); 
    av_register_all(); 
    avformat_network_init(); 

    l_pAVFormatContext = avformat_alloc_context(); 

    l_iResult = avformat_open_input(&l_pAVFormatContext, 
            l_sFile.c_str(), 
            NULL, 
            NULL); 

    if (l_iResult >= 0) 
    { 
     l_iResult = avformat_find_stream_info(l_pAVFormatContext, NULL); 

     if (l_iResult >= 0) 
     { 
      for (int i=0; i<l_pAVFormatContext->nb_streams; i++) 
      { 
       if (l_pAVFormatContext->streams[i]->codec->codec_type == 
         AVMEDIA_TYPE_VIDEO) 
       { 
        l_pCodec = avcodec_find_decoder(
           l_pAVFormatContext->streams[i]->codec->codec_id); 

        l_pStream = l_pAVFormatContext->streams[i]; 
       } 
      } 
     } 
    } 

    av_init_packet(&l_AVPacket); 
    av_init_packet(&l_AVPacket_out); 

    memset(l_auiInBuf + INBUF_SIZE, 0, FF_INPUT_BUFFER_PADDING_SIZE); 

    if (l_pCodec) 
    { 
     l_pAVCodecContext = avcodec_alloc_context3(l_pCodec); 

     l_pParser = av_parser_init(l_pAVCodecContext->codec_id); 

     if (l_pParser) 
     { 
      av_register_codec_parser(l_pParser->parser); 
     } 

     if (l_pAVCodecContext) 
     { 
      if (l_pCodec->capabilities & CODEC_CAP_TRUNCATED) 
      { 
       l_pAVCodecContext->flags |= CODEC_FLAG_TRUNCATED; 
      } 

      l_iResult = avcodec_open2(l_pAVCodecContext, l_pCodec, NULL); 

      if (l_iResult >= 0) 
      { 
       l_pFile_in = fopen(l_sFile.c_str(), "rb"); 

       if (l_pFile_in) 
       { 
        l_pAVFrame = av_frame_alloc(); 
        l_pAVFrameBGR = av_frame_alloc(); 

        if (l_pAVFrame) 
        { 
         l_iFrameCount = 0; 

         avcodec_get_frame_defaults(l_pAVFrame); 

         while (1) 
         { 
          l_iBufLength = fread(l_auiInBuf, 
               1, 
               INBUF_SIZE, 
               l_pFile_in); 

          if (l_iBufLength == 0) 
          { 
           break; 
          } 
          else 
          { 
           l_pData = l_auiInBuf; 
           l_iSize = l_iBufLength; 

           while (l_iSize > 0) 
           { 
            if (l_pParser) 
            { 
             l_iParsedBytes = av_parser_parse2(
                l_pParser, 
                l_pAVCodecContext, 
                &l_AVPacket_out.data, 
                &l_AVPacket_out.size, 
                l_pData, 
                l_iSize, 
                l_AVPacket.pts, 
                l_AVPacket.dts, 
                AV_NOPTS_VALUE); 

             if (l_iParsedBytes <= 0) 
             { 
              break; 
             } 

             l_AVPacket.pts = l_AVPacket.dts = AV_NOPTS_VALUE; 
             l_AVPacket.pos = -1; 
            } 
            else 
            { 
             l_AVPacket_out.data = l_pData; 
             l_AVPacket_out.size = l_iSize; 
            } 

            l_iDecodedBytes = 
              avcodec_decode_video2(
               l_pAVCodecContext, 
               l_pAVFrame, 
               &l_iGotFrame, 
               &l_AVPacket_out); 

            if (l_iDecodedBytes >= 0) 
            { 
             if (l_iGotFrame) 
             { 
              l_pSWSContext = sws_getContext(
                 l_pAVCodecContext->width, 
                 l_pAVCodecContext->height, 
                 l_pAVCodecContext->pix_fmt, 
                 l_pAVCodecContext->width, 
                 l_pAVCodecContext->height, 
                 AV_PIX_FMT_BGR24, 
                 SWS_BICUBIC, 
                 NULL, 
                 NULL, 
                 NULL); 

              if (l_pSWSContext) 
              { 
               l_iResult = avpicture_alloc(
                  reinterpret_cast<AVPicture*>(l_pAVFrameBGR), 
                  AV_PIX_FMT_BGR24, 
                  l_pAVFrame->width, 
                  l_pAVFrame->height); 

               l_iResult = sws_scale(
                  l_pSWSContext, 
                  l_pAVFrame->data, 
                  l_pAVFrame->linesize, 
                  0, 
                  l_pAVCodecContext->height, 
                  l_pAVFrameBGR->data, 
                  l_pAVFrameBGR->linesize); 

               if (l_iResult > 0) 
               { 
                l_cvmImage = cv::Mat(
                   l_pAVFrame->height, 
                   l_pAVFrame->width, 
                   CV_8UC3, 
                   l_pAVFrameBGR->data[0], 
                  l_pAVFrameBGR->linesize[0]); 

                if (l_cvmImage.empty() == false) 
                { 
                 cv::imshow("image", l_cvmImage); 
                 cv::waitKey(10); 
                } 
               } 
              } 

              l_iFrameCount++; 
             } 
            } 
            else 
            { 
             break; 
            } 

            l_pData += l_iParsedBytes; 
            l_iSize -= l_iParsedBytes; 
           } 
          } 

         } // end while(1). 
        } 

        fclose(l_pFile_in); 
       } 
      } 
     } 
    } 
} 

EDIT: Ниже приведен окончательный код, который решает мою проблему, благодаря предложениям Рональда.

#include <iostream> 
#include <iomanip> 
#include <string> 
#include <sstream> 

#include <opencv2/opencv.hpp> 

#ifdef __cplusplus 
extern "C" 
{ 
#endif // __cplusplus 
#include <libavcodec/avcodec.h> 
#include <libavdevice/avdevice.h> 
#include <libavfilter/avfilter.h> 
#include <libavformat/avformat.h> 
#include <libavformat/avio.h> 
#include <libavutil/avutil.h> 
#include <libpostproc/postprocess.h> 
#include <libswresample/swresample.h> 
#include <libswscale/swscale.h> 
#ifdef __cplusplus 
} // end extern "C". 
#endif // __cplusplus 

void main() 
{ 
    AVCodec*   l_pCodec; 
    AVCodecContext*  l_pAVCodecContext; 
    SwsContext*   l_pSWSContext; 
    AVFormatContext* l_pAVFormatContext; 
    AVFrame*   l_pAVFrame; 
    AVFrame*   l_pAVFrameBGR; 
    AVPacket   l_AVPacket; 
    std::string   l_sFile; 
    uint8_t*   l_puiBuffer; 
    int     l_iResult; 
    int     l_iFrameCount; 
    int     l_iGotFrame; 
    int     l_iDecodedBytes; 
    int     l_iVideoStreamIdx; 
    int     l_iNumBytes; 
    cv::Mat    l_cvmImage; 

    l_pCodec = NULL; 
    l_pAVCodecContext = NULL; 
    l_pSWSContext = NULL; 
    l_pAVFormatContext = NULL; 
    l_pAVFrame = NULL; 
    l_pAVFrameBGR = NULL; 
    l_puiBuffer = NULL; 

    l_sFile = "myvideo.ts"; 

    av_register_all(); 

    l_iResult = avformat_open_input(&l_pAVFormatContext, 
            l_sFile.c_str(), 
            NULL, 
            NULL); 

    if (l_iResult >= 0) 
    { 
     l_iResult = avformat_find_stream_info(l_pAVFormatContext, NULL); 

     if (l_iResult >= 0) 
     { 
      for (int i=0; i<l_pAVFormatContext->nb_streams; i++) 
      { 
       if (l_pAVFormatContext->streams[i]->codec->codec_type == 
         AVMEDIA_TYPE_VIDEO) 
       { 
        l_iVideoStreamIdx = i; 

        l_pAVCodecContext = 
          l_pAVFormatContext->streams[l_iVideoStreamIdx]->codec; 

        if (l_pAVCodecContext) 
        { 
         l_pCodec = avcodec_find_decoder(l_pAVCodecContext->codec_id); 
        } 

        break; 
       } 
      } 
     } 
    } 

    if (l_pCodec && l_pAVCodecContext) 
    { 
     l_iResult = avcodec_open2(l_pAVCodecContext, l_pCodec, NULL); 

     if (l_iResult >= 0) 
     { 
      l_pAVFrame = av_frame_alloc(); 
      l_pAVFrameBGR = av_frame_alloc(); 

      l_iNumBytes = avpicture_get_size(PIX_FMT_BGR24, 
              l_pAVCodecContext->width, 
              l_pAVCodecContext->height); 

      l_puiBuffer = (uint8_t *)av_malloc(l_iNumBytes*sizeof(uint8_t)); 

      avpicture_fill((AVPicture *)l_pAVFrameBGR, 
          l_puiBuffer, 
          PIX_FMT_RGB24, 
          l_pAVCodecContext->width, 
          l_pAVCodecContext->height); 

      l_pSWSContext = sws_getContext(
         l_pAVCodecContext->width, 
         l_pAVCodecContext->height, 
         l_pAVCodecContext->pix_fmt, 
         l_pAVCodecContext->width, 
         l_pAVCodecContext->height, 
         AV_PIX_FMT_BGR24, 
         SWS_BICUBIC, 
         NULL, 
         NULL, 
         NULL); 

      while (av_read_frame(l_pAVFormatContext, &l_AVPacket) >= 0) 
      { 
       if (l_AVPacket.stream_index == l_iVideoStreamIdx) 
       { 
        l_iDecodedBytes = avcodec_decode_video2(
           l_pAVCodecContext, 
           l_pAVFrame, 
           &l_iGotFrame, 
           &l_AVPacket); 

        if (l_iGotFrame) 
        { 
         if (l_pSWSContext) 
         { 
          l_iResult = sws_scale(
             l_pSWSContext, 
             l_pAVFrame->data, 
             l_pAVFrame->linesize, 
             0, 
             l_pAVCodecContext->height, 
             l_pAVFrameBGR->data, 
             l_pAVFrameBGR->linesize); 

          if (l_iResult > 0) 
          { 
           l_cvmImage = cv::Mat(
              l_pAVFrame->height, 
              l_pAVFrame->width, 
              CV_8UC3, 
              l_pAVFrameBGR->data[0], 
             l_pAVFrameBGR->linesize[0]); 

           if (l_cvmImage.empty() == false) 
           { 
            cv::imshow("image", l_cvmImage); 
            cv::waitKey(1); 
           } 
          } 
         } 

         l_iFrameCount++; 
        } 
       } 
      } 
     } 
    } 
} 
+0

когда вы используете ffmpeg на нем, нет ошибок? – rogerdpack

+0

Да. конечно. Даже если я использую ffplay, видео декодируется без ошибок, и все как ожидается. – arms

+0

чувствует, как будто он проигрывает бит каким-то образом ... хм ... gl! – rogerdpack

ответ

1

Вы никогда не используете объект l_pParser, или другими словами, вы не используете H264 парсер, вы просто передать исходные данные файла в декодер без надлежащего NAL пакетирования. Пожалуйста, прочитайте frame parsing API docs, чтобы выяснить, как использовать синтаксический анализатор.

+0

Спасибо Рональду за ваши предложения. К сожалению, даже с парсером (как вы также предположили в вопросе http://stackoverflow.com/questions/29406888/ffmpeg-c-h264-decoding-error), я получаю такое же большое количество ошибок, и кадр неправильно декодирован (Я получаю такое же серое изображение с некоторыми артефактами). Я обновил код в исходном вопросе, включая управление парсером. Большое вам спасибо за вашу поддержку! – arms

+0

Ваш код работает для меня, единственный комментарий, который у меня есть, заключается в том, что между вашими вызовами av_parser_parse2() и avcodec_decode_video2() вы должны проверить, будет ли l_iParsedBytes <= 0, и если это так, вырваться из цикла. Но даже без этой проверки код работает отлично для меня в двух тестовых файлах. –

+0

Собственно, подождите минуту, извините, просто заметив сейчас, но этот код работает только для файлов H264 (applicationb). Ваш входной файл является .ts-файлом. Parsers не могут читать файлы .ts, просто анализировать содержимое raw h264/applicationb. Для .ts вам нужен полноэкранный .ts demuxer, который вы найдете в libavformat. –