ChinaFFmpeg

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 11575|回复: 1

[源码] 从hevc编码格式的视频里解帧只能解一半?

[复制链接]
发表于 2019-8-2 17:50:12 | 显示全部楼层 |阅读模式
我视频格式信息大致如下:
[AppleScript] 纯文本查看 复制代码
[NULL @ 0x564aa13ec600] PPS id out of range: 0
[hevc @ 0x564aa13ec600] PPS id out of range: 0
[hevc @ 0x564aa13ec600] Error parsing NAL unit #0.
[hevc @ 0x564aa13ec600] PPS id out of range: 0
    Last message repeated 1 times
[hevc @ 0x564aa13ec600] Error parsing NAL unit #0.
[hevc @ 0x564aa13ec600] PPS id out of range: 0
    Last message repeated 1 times
[hevc @ 0x564aa13ec600] Error parsing NAL unit #0.
[hevc @ 0x564aa13ec600] PPS id out of range: 0
    Last message repeated 1 times
[hevc @ 0x564aa13ec600] Error parsing NAL unit #0.
[hevc @ 0x564aa13ec600] PPS id out of range: 0
    Last message repeated 1 times
[hevc @ 0x564aa13ec600] Error parsing NAL unit #0.
Input #0, mpeg, from xxxxx.mp4':
  Duration: 00:23:39.85, start: 20018.316689, bitrate: 3024 kb/s
    Stream #0:0[0x1e0]: Video: hevc (Main), yuv420p(tv), 2560x1536, 50 fps, 50 tbr, 90k tbn, 50 tbc


然后我使用如下的代码用ffmpeg把每一帧解出来转opencv Mat然后做处理,但是只能解出一半,比如视频总70590帧,最后只能解35295帧出来,但是换了其他别的h264 h265等mp4视频都是正常没问题的,这个是咋回事,大师兄知道要怎么修改这个代码嘛?使用的是海康威视的摄像头获取的监控视频。但是我使用这个命令:
[AppleScript] 纯文本查看 复制代码
ffmpeg -i xxxxx.mp4 -f image2 %d.jpg


却可以把全部帧完整解出来。
下面是我使用的只能解出一半帧的代码:
[C++] 纯文本查看 复制代码
/**
SZU FML lab
author:Jinlong Li
*
*/
#define __STDC_CONSTANT_MACROS
#include <stdio.h>
#include <cstdio>
#include <fstream>
#include <algorithm>
#include <iomanip>

#include <opencv/cv.h>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

#include <experimental/filesystem>
#include <gflags/gflags.h>

/* 添加ffmpeg头文件 */
#include <math.h>
extern "C"
{
// 设置操作选项操作
#include <libavutil/opt.h>
#include <libavcodec/avcodec.h>
// 用于音频声道布局操作
#include <libavutil/channel_layout.h>
#include <libavutil/common.h>
#include <libavutil/imgutils.h>
// 数学相关操作
#include <libavutil/mathematics.h>
#include <libavutil/samplefmt.h>
// 封装和解封装操作
#include <libavformat/avformat.h>
// 用于缩放和转换颜色格式操作
#include <libswscale/swscale.h>
}

/* 获取时间然后对比 */
double what_time_is_it_now()
{
    struct timeval time;
    if (gettimeofday(&time,NULL)){
        return 0;
    }
    return (double)time.tv_sec + (double)time.tv_usec * .000001;
}

int main(int argc, char **argv)
{
    
    time_t t;
    t = time(NULL);
    std::cout << "Start time : " << ctime(&t);
    double start_time = what_time_is_it_now();

    /* register all the codecs */
    /* 一般都是要函数内部初始化操作 */
    av_register_all();

    if (argc < 2) {
        printf("usage: %s input_file\n"
               "example: ./decoding_mp4.bin hello.mp4",
               argv[0]);
        exit(1);
    }
    /* 定义操作的上下文线索 */
    AVFormatContext* pFormatCtx = NULL;
    const char*filename = argv[1];
    std::string videoName(filename);
    //step 1:open file,get format info from file header
    // 打开文件然后获取格式信息
    if (avformat_open_input(&pFormatCtx, filename, NULL, NULL) != 0){
        fprintf(stderr,"avformat_open_input");
        exit(1);
    }
    //step 2:get stread info
    // 这里输出视频流的信息
    if (avformat_find_stream_info(pFormatCtx, NULL) < 0){
        fprintf(stderr,"avformat_find_stream_info");
        exit(1);
    }
    //just output format info of input file
    av_dump_format(pFormatCtx, 0, filename, 0);
    int videoStream = -1;
    //step 3:find vido stream
    // 找到视频流数据,然后输出相关信息,包括视频流的宽高,总帧数,帧率等
    int stream_width, stream_heigth, fps;
    long total_frames;
    for (int i = 0; i < pFormatCtx->nb_streams; i++)
    {
        AVStream*in_stream = pFormatCtx->streams[i];
        // fps = pFormatCtx->streams[i]->r_frame_rate;
        fps = in_stream->avg_frame_rate.num / in_stream->avg_frame_rate.den;
        if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            videoStream = i;
            stream_width = in_stream->codec->width;
            stream_heigth = in_stream->codec->height;
            total_frames = in_stream->nb_frames;
            break;
        }
    }
    /* 获取视频时间长度 */
    long long video_duration;
    if(pFormatCtx->duration > 0)
    {
        video_duration = pFormatCtx->duration / 1000LL;
    }
    else{
        video_duration = 0;
    }
    long long check_total_frames = video_duration/1000*fps;
    if(total_frames < check_total_frames)
    {
        total_frames = check_total_frames;
    }
    std::cout << "total_frames = " << total_frames << "  fps = " << fps << std::endl;

    if (videoStream == -1){
        fprintf(stderr,"find video stream error");
        exit(1);
    }
    AVCodecContext* pCodecCtxOrg = NULL;
    AVCodecContext* pCodecCtx = NULL;

    AVCodec* pCodec = NULL;
    // 编解码
    pCodecCtxOrg = pFormatCtx->streams[videoStream]->codec; // codec context        
    //step 4:find  decoder
    // 查找解码器
    pCodec = avcodec_find_decoder(pCodecCtxOrg->codec_id);
    std::cout << "pCoder : " << pCodec << std::endl;

    if (!pCodec){
        fprintf(stderr,"avcodec_find_decoder error");
        exit(1);
    }
    //step 5:get one instance of AVCodecContext,decode need it.
    pCodecCtx = avcodec_alloc_context3(pCodec);
    if (avcodec_copy_context(pCodecCtx, pCodecCtxOrg) != 0){
        fprintf(stderr,"avcodec_copy_context error");
        exit(1);
    }
    //step 6: open codec
    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0){
        fprintf(stderr,"avcodec_open2 error");
        exit(1);
    }
    AVFrame* pFrame = NULL;
    AVFrame* pFrameRGB = NULL;

    pFrame = av_frame_alloc();
    pFrameRGB = av_frame_alloc();

    int numBytes = 0;
    uint8_t* buffer = NULL;
    /* 转换为和opencv对应的数据格式:AV_PIX_FMT_RGB24 */
    numBytes = avpicture_get_size(AV_PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height);
    buffer = (uint8_t*)av_malloc(numBytes * sizeof(uint8_t));

    avpicture_fill((AVPicture*)pFrameRGB, buffer, AV_PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height);

    struct SwsContext* sws_ctx = NULL;
    /* 可以看作是sws_scale函数初始化函数 */
    // std::cout << pCodecCtx->pix_fmt << std::endl;
    // std::cout << AV_PIX_FMT_YUV420P << std::endl;
    sws_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
            pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_BGR24, SWS_BILINEAR, NULL, NULL, NULL);

    AVPacket packet;
    long long i = 1;
    //step 7:read frame
    std::ofstream outfile;
    /* 外面创建这个对象,避免每次循环都要创建一次 */
    cv::Mat mRGB(cv::Size(pCodecCtx->width, pCodecCtx->height), CV_8UC3);
    while (av_read_frame(pFormatCtx, &packet) >= 0)
    {
        int frameFinished = 0;
        avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);
        if (frameFinished)
        {
            /* 可以看作是sws_getContext函数的执行函数 */
            sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data, pFrame->linesize, 0,
            pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);

            if (i <= total_frames)
            {
                //step 8:save frame
                // mRGB.data = buffer;
                /* 直接让Mat矩阵的头指针指向pFrameRGB可以更快些,避免了内存数据的拷贝 */
                mRGB.data = (uchar*)pFrameRGB->data[0];
                /* RGB格式转opencv BGR格式 */
                // cv::cvtColor(mRGB, mRGB, cv::COLOR_RGB2BGR);
                // select_ROI(mRGB);

                std::cout << "process frame : " << i << std::endl;
                i++;
                if(i > total_frames)
                    break;
                
            }
            
        }
    }
    //release resource
    av_free_packet(&packet);

    av_free(buffer);
    av_frame_free(&pFrameRGB);

    av_frame_free(&pFrame);

    avcodec_close(pCodecCtx);
    avcodec_close(pCodecCtxOrg);

    // sws_freeContent(sws_ctx);

    avformat_close_input(&pFormatCtx);

    double all_time = what_time_is_it_now() - start_time;
    return 0;
}


还请大师兄可以解答下~~

回复

使用道具 举报

发表于 2019-8-7 08:28:42 | 显示全部楼层
从出错信息看,似乎是解析NAL信息的姿势不对,具体的代码我也没看,可以参考ffmpeg的doc/examples/x下面的decode相关例子
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|Archiver|ChinaFFmpeg

GMT+8, 2024-4-20 13:49 , Processed in 0.056910 second(s), 15 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表