fan-chao / nvjmi

封装Jetson Multimedia API的编解码库,基于https://github.com/jocover/jetson-ffmpeg基础进行的修改,未集成于ffmpeg,可单独使用。
MIT License
31 stars 12 forks source link

作者你好,请问怎么这个项目能不能转换yuv420p的颜色空间 #9

Closed zjhellofss closed 2 years ago

zjhellofss commented 2 years ago

流的封装是h264,传输的协议是rtsp,然后颜色空间是yuv420p。但是我用这个项目转换后得到的图像是条黑白纹状的无意义图像。我想知道是否本项目只支持nv12的颜色空间?

zjhellofss commented 2 years ago

代码和目前转换的结果如下:

#include <string>
#include "nvjmi.h"
#include "opencv2/opencv.hpp"
#include <cstdio>
#include "opencv2/cudawarping.hpp"
#include <string>

#define __STDC_CONSTANT_MACROS
#ifdef __cplusplus
extern "C"
{
#endif
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
#ifdef __cplusplus
};
#endif

#include <chrono>

#define DEBUG true
typedef std::chrono::high_resolution_clock clock_;
typedef std::chrono::duration<float, std::milli> duration;

double ticks(clock_::time_point start) {
    duration elapsed = clock_::now() - start;
    return elapsed.count();
}

AVFormatContext *open_rtsp(const std::string &rtsp_address, const std::string &player_base) {
    AVFormatContext *ifmt_ctx = nullptr;
    AVStream *st = nullptr;
    char err_buf[512];
    AVDictionary *optionsDict = nullptr;
    av_register_all();
    avformat_network_init();
    av_dict_set(&optionsDict, "rtsp_transport", "tcp", 0);
    av_dict_set(&optionsDict, "stimeout", "2000000", 0); ///设置微秒超时时间
    int ret;
    if ((ret = avformat_open_input(&ifmt_ctx, rtsp_address.data(), 0, &optionsDict)) <
        0) {
        printf("Could not open input file '%s' (error '%s')\n", rtsp_address.data(),
               av_make_error_string(err_buf, sizeof(err_buf), ret));
        return nullptr;
    }

    if ((ret = avformat_find_stream_info(ifmt_ctx, nullptr)) <
        0) {
        printf("Could not open find stream info (error '%s')\n", av_make_error_string(err_buf, sizeof(err_buf), ret));
        return nullptr;
    }

    for (int i = 0; i < ifmt_ctx->nb_streams; i++) {
        av_dump_format(ifmt_ctx, i, rtsp_address.c_str(), 0);
    }

    bool has_264_video = false;
    for (int i = 0; i < ifmt_ctx->nb_streams; i++) {
        st = ifmt_ctx->streams[i];
        switch (st->codec->codec_type) {
            case AVMEDIA_TYPE_AUDIO:
                break;
            case AVMEDIA_TYPE_VIDEO:
                has_264_video = true;
                break;
            default:
                break;
        }
    }
    if (has_264_video) {
        return ifmt_ctx;
    } else {
        return nullptr;
    }
}

int main() {

    int height = 1920;
    int width = 1080;
    std::string rtsp_address = "rtsp://192.168.4.8:57554/scv/vfs/group-1634290456300783180/group-1634290456300783180_ch0";
    auto fmt_ctx = open_rtsp(rtsp_address, "");
    if (fmt_ctx) {
        printf("Open successfully\n");
    } else {
        printf("Open failed");
        // todo
    }
    AVPacket pkt;
    av_init_packet(&pkt);

    jmi::nvJmiCtxParam jmi_ctx_param;
    if (width > 0 && height > 0) {
        jmi_ctx_param.resize_width = width;
        jmi_ctx_param.resize_height = height;
    }

    jmi_ctx_param.coding_type = jmi::NV_VIDEO_CodingH264;
    std::string dec_name = "test_dec";
    jmi::nvJmiCtx *jmi_ctx_ = jmi::nvjmi_create_decoder(dec_name.data(), &jmi_ctx_param);
    jmi::nvPacket nvpacket;
    auto start = clock_::now();

    bool decode_success = false;
    cv::Mat output_frame;
    int index;
    while (true) {
        int ret = av_read_frame(fmt_ctx, &pkt);
        if (ret != 0) {
            break;
        } else {
            auto payload = pkt.data;
            auto payload_size = pkt.size;
            nvpacket.payload_size = payload_size;
            nvpacket.payload = payload;
            cv::Mat frame = cv::Mat(height, width, CV_8UC3);
            if (payload != nullptr) {
                ret = jmi::nvjmi_decoder_put_packet(jmi_ctx_, &nvpacket);
                while (ret >= 0) {
                    jmi::nvFrameMeta nvframe_meta;
                    ret = jmi::nvjmi_decoder_get_frame_meta(jmi_ctx_, &nvframe_meta);
                    if (ret < 0) {
                        break;
                    }
                    ret = jmi::nvjmi_decoder_retrieve_frame_data(jmi_ctx_, &nvframe_meta, (void *) frame.data);
                    if (!ret) {
                        frame.download(output_frame);
                        cv::imwrite( std::to_string(index) + ".jpg", frame);
                        index += 1;
                    }
                }
            }
        }
    }

}

104

fan-chao commented 2 years ago

这个我这边只用到了nv12。转换为yuv420p可以看看有没有对应的参数,如果没有,应该也可以使用cuda能够转成yuv420p。

zjhellofss commented 2 years ago

作者有没有做过相关测试,在jetson上的硬解和软解的速度比较。

fan-chao commented 2 years ago

硬解比软件快至少3倍。

CMangoDH commented 2 years ago

感谢作者的贡献 我在使用ffmpeg接收rtsp数据流后使用该项目也是播不出来,参照了/usr/src/jetson_multimedia_api/samples/00_video_decode中的代码: 修改nvjmi_dec.cpp文件 ret = ctx->dec->setFrameInputMode(0) 修改为 ret = ctx->dec->setFrameInputMode(1) 因为av_read_frame返回的都是完整的nalu单元