苗火 Nicholas
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;
}
发表评论:
昵称

邮件地址 (选填)

个人主页 (选填)

内容