Get Base64 codec images from RTSP

2025-6-20 写技术

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
#include <libavutil/base64.h>
#include <libavutil/mem.h>

static const char base64_table[] = 
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";




void save_frame_as_image(AVFrame *frame, const char *filename) {
    // 查找编码器(示例使用JPEG编码器)
    AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_MJPEG);
    AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);
    
    // 设置编码参数
    codec_ctx->width = frame->width;
    codec_ctx->height = frame->height;
    codec_ctx->pix_fmt = AV_PIX_FMT_YUVJ420P; // JPEG兼容格式
    codec_ctx->time_base = (AVRational){1, 25};
    
    // 打开编码器
    if (avcodec_open2(codec_ctx, codec, NULL) < 0) {
        fprintf(stderr, "无法打开编码器\n");
        return;
    }

    // 创建包对象
    AVPacket pkt;
    av_init_packet(&pkt);
    pkt.data = NULL;
    pkt.size = 0;

    // 发送帧到编码器
    if (avcodec_send_frame(codec_ctx, frame) < 0) {
        fprintf(stderr, "编码错误\n");
        return;
    }

    // 接收编码数据
    printf(" before save file pkt.size=%d\n", pkt.size);
    if (avcodec_receive_packet(codec_ctx, &pkt) == 0) {
        printf(" save file pkt.size=%d\n", pkt.size);
        // 写入文件
        FILE *file = fopen(filename, "wb");
        fwrite(pkt.data, 1, pkt.size, file);
        fclose(file);
    }
    
    av_packet_unref(&pkt);
    avcodec_free_context(&codec_ctx);
}



char * frame_to_base64(AVFrame *frame) {
    // 查找编码器(示例使用JPEG编码器)
    printf(" nicholas 1 \n");
    AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_MJPEG);
    AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);
    printf(" nicholas 2 \n");
    // 设置编码参数
    codec_ctx->width = frame->width;
    codec_ctx->height = frame->height;
    codec_ctx->pix_fmt = AV_PIX_FMT_YUVJ420P; // JPEG兼容格式
    codec_ctx->time_base = (AVRational){1, 25};
    
    // 打开编码器
    if (avcodec_open2(codec_ctx, codec, NULL) < 0) {
        printf("can't open codec\n");
        return NULL;
    }
    printf(" nicholas 3.0 \n");

    // 创建包对象
    AVPacket pkt;
    av_init_packet(&pkt);
    printf(" nicholas 3.1\n");
    pkt.data = NULL;
    pkt.size = 0;

    // 发送帧到编码器
    if (avcodec_send_frame(codec_ctx, frame) < 0) {
        printf("codec error\n");
        return NULL;
    }
printf(" nicholas 4 \n");
 
    // 接收编码数据
    printf(" before receive  pkt.size=%d\n", pkt.size);
    if (avcodec_receive_packet(codec_ctx, &pkt) != 0) {
        printf(" save file pkt.size=%d\n", pkt.size);
        av_packet_unref(&pkt);
        avcodec_free_context(&codec_ctx);
        return NULL;
    }    
    printf(" save file pkt.size=%d\n", pkt.size);


    // Base64编码
    //int b64_len = (int)av_base64_encode(NULL, 0, aligned_data, data_size);
    int b64_len = AV_BASE64_SIZE(pkt.size); //4 * ((data_size + 2) / 3);

    printf(" receive pkt.size=%d", pkt.size);
  
    char *b64_data = av_malloc(b64_len + 1); // 包含NULL终止符
    printf(" nicholas 5 \n");
    av_base64_encode(b64_data, b64_len + 1, pkt.data, pkt.size);                

   printf(" nicholas 6 \n");                

        int i=0;
        char path[250]={0};
    if(i >100){
        return 0;
    }
    i++;
    sprintf(path, "images/N%d.html", i);
    FILE* file = fopen(path, "w");
    if (file == NULL) {
        perror("Error opening file");
        return 0;
    }
    
    printf(" nicholas 7 \n");
    fprintf(file, "<img src='data:image/png;base64,%s' />", b64_data);
    fclose(file);

    printf(" ********888nicholas \n");

    return 0;






    av_packet_unref(&pkt);
    avcodec_free_context(&codec_ctx);
}



void save_rgb(AVFrame *rgb_frame, AVFrame *frame){
    

   // 写入BMP文件(无压缩)
    FILE *file = fopen("test2.png", "wb");
    if(file) {
        // BMP文件头
        unsigned char header[54] = {
            0x42,0x4D,0,0,0,0,0,0,0,0,54,0,0,0,40,0,0,0,
            0,0,0,0,0,0,0,0,1,0,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
        };
        int filesize = 54 + 3 * frame->width * frame->height;
        header[2] = (unsigned char)(filesize);
        header[3] = (unsigned char)(filesize>>8);
        header[4] = (unsigned char)(filesize>>16);
        header[5] = (unsigned char)(filesize>>24);
        header[18] = (unsigned char)(frame->width);
        header[19] = (unsigned char)(frame->width>>8);
        header[20] = (unsigned char)(frame->width>>16);
        header[21] = (unsigned char)(frame->width>>24);
        header[22] = (unsigned char)(frame->height);
        header[23] = (unsigned char)(frame->height>>8);
        header[24] = (unsigned char)(frame->height>>16);
        header[25] = (unsigned char)(frame->height>>24);

        fwrite(header, 1, 54, file);
        
        // 写入RGB数据(需处理linesize可能存在的padding)
        for(int y=frame->height-1; y>=0; y--) {
            fwrite(rgb_frame->data[0] + y*rgb_frame->linesize[0], 1, 3*frame->width, file);
        }
        fclose(file);
    }

    return ;

}

char* yuv_to_base64(AVFrame *frame) {
    if (!frame || !frame->data[0]) return NULL;

    char *b64 = frame_to_base64(frame);
    printf("ooook\n");



    save_frame_as_image(frame, "nnnnnnnnnnn2.png");


    printf(" org format:%d, width:%d, height:%d\n", frame->format, frame->width, frame->height);

    //save_frame_as_image(frame, "test.png");
    //return 0;
    
    // 转换为RGB格式确保浏览器兼容
    struct SwsContext *sws_ctx = sws_getContext(
        frame->width, frame->height, frame->format,
        frame->width, frame->height, AV_PIX_FMT_RGB24,
        SWS_BILINEAR | SWS_ACCURATE_RND, NULL, NULL, NULL);

    if (!sws_ctx) {
        printf("Failed to create sws context\n");
        return NULL;
    }    
    
    AVFrame *rgb_frame = av_frame_alloc();
    rgb_frame->format = AV_PIX_FMT_RGB24;
    rgb_frame->width = frame->width;
    rgb_frame->height = frame->height;
    av_frame_get_buffer(rgb_frame, 32);
    
    // 实际执行像素格式转换
    sws_scale(sws_ctx, (const uint8_t* const*)frame->data, frame->linesize, 0, 
              frame->height, rgb_frame->data, rgb_frame->linesize);




    if(rgb_frame->format != AV_PIX_FMT_RGB24) {
        return NULL;
    }



    printf(" hello2\n");
    save_rgb(rgb_frame, frame);
    save_frame_as_image(rgb_frame, "nnnnnnnnnnn.png");

    frame_to_base64(rgb_frame);
    printf(" bye22\n");
    return NULL;




    // 计算实际数据长度(处理linesize padding)
    int data_size = rgb_frame->width * rgb_frame->height * 3;
    uint8_t *aligned_data = av_malloc(data_size);
    av_image_copy_to_buffer(aligned_data, data_size,
                          (const uint8_t* const*)rgb_frame->data, rgb_frame->linesize,
                          AV_PIX_FMT_RGB24,
                          rgb_frame->width, rgb_frame->height, 1);

    



  





    // Base64编码
    //int b64_len = (int)av_base64_encode(NULL, 0, aligned_data, data_size);
    int b64_len = AV_BASE64_SIZE(data_size); //4 * ((data_size + 2) / 3);
    printf(" len=%d\n",b64_len);
    char *b64_data = av_malloc(b64_len + 1); // 包含NULL终止符
    av_base64_encode(b64_data, b64_len + 1, aligned_data, data_size);                

                   

        int i=0;
        char path[250]={0};
    if(i >100){
        return 0;
    }
    i++;
    sprintf(path, "images/%d.html", i);
    FILE* file = fopen(path, "w");
    if (file == NULL) {
        perror("Error opening file");
        return 0;
    }
    
    fprintf(file, "<img src='data:image/png;base64,%s' />", b64_data);
    fclose(file);

    return 0;





    // 创建对齐的缓冲区
    int aligned_width = FFALIGN(frame->width, 32);
    uint8_t *aligned_buffer = av_malloc(aligned_width * frame->height * 3);
    
    // 将RGB数据复制到对齐的缓冲区
    av_image_copy_to_buffer(aligned_buffer, aligned_width * frame->height * 3,
                       (const uint8_t* const*)rgb_frame->data, rgb_frame->linesize,
                       AV_PIX_FMT_RGB24, frame->width, frame->height, 1);

    // 使用对齐后的buffer进行base64编码
    int in_len = frame->width * frame->height * 3;  // RGB24数据大小
    uint8_t *in = aligned_buffer;
    int out_len = 4 * ((in_len + 2) / 3);
    char *b64_str = malloc(out_len + 1);
    
    for (int i=0, j=0; i<in_len;) {
        uint32_t triple = (i<in_len?in[i++]:0)<<16;
        triple |= (i<in_len?in[i++]:0)<<8;
        triple |= (i<in_len?in[i++]:0);
        b64_str[j++] = base64_table[(triple>>18)&0x3F];
        b64_str[j++] = base64_table[(triple>>12)&0x3F];
        b64_str[j++] = base64_table[(triple>>6)&0x3F];
        b64_str[j++] = base64_table[triple&0x3F];
    }
    
    for (int pad=in_len%3; pad; pad--)
        b64_str[out_len-pad] = '=';
    b64_str[out_len] = '\0';
    
    // 释放资源
    sws_freeContext(sws_ctx);
    av_frame_free(&rgb_frame);
    av_free(aligned_buffer);
    
    return b64_str;
}









char* packet_to_base64(AVPacket *pkt) {
    if (!pkt || !pkt->data[0]) return NULL;





    // Base64编码
    //int b64_len = (int)av_base64_encode(NULL, 0, aligned_data, data_size);
    int b64_len = AV_BASE64_SIZE(pkt->size); //4 * ((data_size + 2) / 3);
  
    char *b64_data = av_malloc(b64_len + 1); // 包含NULL终止符
    av_base64_encode(b64_data, b64_len + 1, pkt->data, pkt->size);                

                   

        int i=0;
        char path[250]={0};
    if(i >100){
        return 0;
    }
    i++;
    sprintf(path, "images/%d.html", i);
    FILE* file = fopen(path, "w");
    if (file == NULL) {
        perror("Error opening file");
        return 0;
    }
    
    fprintf(file, "<img src='data:image/png;base64,%s' />", b64_data);
    fclose(file);

    return 0;



    
    return b64_data;
}





int main() {
    AVFormatContext *fmt_ctx = NULL;
    AVCodecContext *codec_ctx = NULL;
    AVCodec *codec = NULL;
    AVFrame *frame = av_frame_alloc();
    AVPacket *pkt = av_packet_alloc();


    char path[250] = {0};
    int i = 0;
    
    avformat_network_init();
    // 初始化解码器
    AVDictionary* options = NULL;
    av_dict_set(&options, "rtsp_transport", "tcp", 0);
    av_dict_set(&options, "stimeout", "5000000", 0);   // 设置超时5秒

    fmt_ctx = avformat_alloc_context();
    fmt_ctx->probesize = 10000000;  // 将探测大小增至10MB
    fmt_ctx->max_analyze_duration = 5 * AV_TIME_BASE;  // 设置5秒分析时长

    avformat_open_input(&fmt_ctx, "rtsp://stream.strba.sk:1935/strba/VYHLAD_JAZERO.stream", NULL, &options);
    avformat_find_stream_info(fmt_ctx, NULL);
    int video_stream = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &codec, 0);

    //AVCodecParameters *codecpar = fmt_ctx->streams[video_stream]->codecpar;
    //codec = avcodec_find_decoder(codecpar->codec_id);

    codec_ctx = avcodec_alloc_context3(codec);
    avcodec_parameters_to_context(codec_ctx, fmt_ctx->streams[video_stream]->codecpar);
    avcodec_open2(codec_ctx, codec, NULL);
    
    // 处理流程
    while (av_read_frame(fmt_ctx, pkt) >= 0) {
        if (pkt->stream_index == video_stream) {
            avcodec_send_packet(codec_ctx, pkt);
            if (avcodec_receive_frame(codec_ctx, frame) == 0) {
                char *b64 = yuv_to_base64(frame);


                //printf("<img src=\"data:image/jpeg;base64,%s\">\n", b64);
                free(b64);
            }
        }
        av_packet_unref(pkt);
    }
    
    // 释放资源
    av_frame_free(&frame);
    av_packet_free(&pkt);
    avcodec_free_context(&codec_ctx);
    avformat_close_input(&fmt_ctx);
    return 0;
}

标签: C++

发表评论:

Powered by anycle 湘ICP备15001973号-1