libav c ++ raw h264 TCP Stream для opencv Mat - PullRequest
0 голосов
/ 10 ноября 2018

Я пишу программное обеспечение для моих классов, которое получает поток h264 от дрона, и мне нужно преобразовать видеопоток в opencv Mat.

У меня нет проблем с получением кадра, и если я сохраню его в файл .264, я смогу прочитать вывод с помощью VLC.

Дрон отправляет IDR-кадр и P-кадр, так как мне не нужно видеть, что видеопоток оправдывает какой-то его кадр, я думал только об использовании IDR-кадра для получения изображения, но у меня возникли проблемы чтобы понять, как использовать libavcodec из FFMPEG, как я могу создать AVFrame из моего IDR-кадра и как преобразовать его в cv :: Mat после.

Я пробовал следующий код, я храню полный кадр в файле .raw и пытаюсь их декодировать, но я получаю ошибку чтения, когда пытаюсь проанализировать пакет во фрейме, я не запускаю инициализацию буфер ARPacket правильный путь:

AVFormatContext* fc = 0;
int vi = -1; // vi veut dire video index


inline cv::Mat avframe_to_mat(const AVFrame* avframe) 
{
    AVFrame dst;
    cv::Mat m;

memset(&dst, 0, sizeof(dst));

int w = avframe->width, h = avframe->height;

m = cv::Mat(h, w, CV_8UC3);
dst.data[0] = (uint8_t*)m.data;

avpicture_fill((AVPicture*)&dst, dst.data[0], AV_PIX_FMT_BGR24, w, h);

struct SwsContext *convert_ctx = NULL;

enum AVPixelFormat src_pixfmt = AV_PIX_FMT_BGR24;
enum AVPixelFormat dst_pixfmt = AV_PIX_FMT_BGR24;

convert_ctx = sws_getContext(w, h, src_pixfmt, w, h, dst_pixfmt, SWS_FAST_BILINEAR, NULL, NULL, NULL);

sws_scale(convert_ctx, avframe->data, avframe->linesize, 0, h, dst.data, dst.linesize);
sws_freeContext(convert_ctx);

return m;

}

inline bool init_stream(unsigned char* data, int len)
{

const char* file = "test.avi";

const AVCodecID codec_id = AV_CODEC_ID_H264;
AVCodec* codec = avcodec_find_encoder(codec_id);
// Crée le container pour le stream
fc = avformat_alloc_context();

/*
AVOutputFormat *of = av_guess_format(0, file, 0);
fc = avformat_alloc_context();
fc->oformat = of;
strcpy(fc->filename, file);
*/
int br = 1000000;
int w = 640;
int h = 360;

int fps = 24;


// ajoute un stream video
AVStream* pst = avformat_new_stream(fc, codec); // Pourquoi je passe pas le codec ici ?
vi = pst->index;

codec_context = avcodec_alloc_context3(codec);

codec_context->bit_rate = br;
codec_context->width = w;
codec_context->height = h;
codec_context->time_base = {1,fps};
codec_context->gop_size = 10; // Emit one intra frame every ten frames
codec_context->max_b_frames = 1;
codec_context->pix_fmt = AV_PIX_FMT_YUV420P;

// Vu quon n'est en h264
av_opt_set(codec_context->priv_data, "preset", "slow", 0);


// Ouvre notre codec    
if(avcodec_open2(codec_context, codec,nullptr) < 0)
{
    cerr << "Impossible d'ouvrir le codec" << endl;
    return false;
}

if (!(fc->oformat->flags & AVFMT_NOFILE))
    avio_open(&fc->pb, fc->filename,0);

//  avformat_write_header(fc,nullptr);


return true;
}


inline void append_stream(uint8_t* data, int len)
{
if( 0 > vi)
{
    cerr << "video index is less than 0" << endl;
    return;
}
AVStream* pst = fc->streams[vi];

AVPacket pkt;


// Init un nouveau packet
av_init_packet(&pkt);
pkt.flags |= AV_PKT_FLAG_KEY;
pkt.data = data;
pkt.stream_index = pst->index;
pkt.size = len;

pkt.dts = AV_NOPTS_VALUE;
pkt.pts = AV_NOPTS_VALUE;

// ERROR accessing location
av_interleaved_write_frame(fc, &pkt);


}


inline void execute_staging_test(const fs::path& folder, int nbr_trame)
{
fs::path file_name = folder / "stream.bin";
if(!fs::exists(file_name))
{
    cerr << "The file " << file_name.string() << " does not exists" << endl;
    return;
}

avcodec_register_all();

av_log_set_level(AV_LOG_DEBUG);
int length = 0;
char* buffer;

for(int i = 0; i < nbr_trame;i++)
{
    fs::path file = std::to_string(i) + ".raw";
    file = folder / file;
    cout << "Got frame on " << file.string() << endl;
    ifstream ifs(file, ofstream::binary);

    // Get la longeur du fichier pour savoir le buffer a prendre
    ifs.seekg(0, ios::end);
    length = ifs.tellg();
    ifs.seekg(0, ios::beg);

    if (length == 0) {
        std::cerr << "No data in file " << file << std::endl;
        return;
    }

    buffer = new char[length];

    std::cout << "File " << file << " length is " << length << std::endl;

    ifs.read(buffer, length);

    cv::VideoWriter vw;
    int codec = cv::VideoWriter::fourcc('X', '2', '6', '4');


    if(!fc)
    {
        if(!init_stream((unsigned char*)buffer, length))
        {
            return;
        }
    }
    if(fc)
    {
        append_stream((unsigned char*)buffer, length);
    }   
}
}

Спасибо вам большое, если вы можете мне помочь, я новичок в C ++, и я никогда не имел дело с видео потоком. Если вы хотите увидеть полный код его хоста на github repo к этому проекту

...