ARM11硬件编解码(MFC)例程

 

 




摘要:介绍基于华恒科技HHS3C6410平台,将摄像头采集的数据通过硬件编码后通过网络发送,客户端(Linux)通过网络实时接收并显示的具体实现;主要介绍S3C6410的H264硬件编码特性和V4L2编程。

关键词:S3C6410 H.264 视频监控 V4L2

0 引言
    HHS3C6410是华恒科技推出的一款针对高性能手持设备和通用视频处理应用的低功率, 高性能的嵌入式开发板,采用三星S3C6410 ARM1176JZF-S处理器,最高主频可达667MHz;S3C6410内部集成的多媒体编解码器(MFC)支持mpeg4/h.263/h.264的编码与解码,并支持VC1解码,性能可以达到全双工30fps@640x480同时编解码和半双工30fps@720x480或25fps@720x576编解码。
    H.264/AVC标准是一套兼顾广播和电信,覆盖从低码率通信到高清晰电视的广域标准,相比以前的标准,具有更高的压缩率,高质量图像,容错功能,并有很强的网络适应性。

1 总体设计
    监控系统由监控前端,监控终端,网络三部分组成,监控前端是一个嵌入式Linux系统,它通过S3C6410的Camera IF接收摄像头(SAA7113)采集的数据,并传送给硬件编解码(MFC)模块,并把得到的经过H264压缩的数据打包发送到IP网络上,监控终端(Linux)通过网络接收数据包,经过解码实时播放。总体框架如图1:

图1. 总体框架图
整个嵌入式视频采集系统由软件和硬件两部分组成。硬件方面,以S3C6410处理器为核心通过Camera IF接口接收摄像头数据,经过MFC硬件编码后通过DM9000发送,UART作为开发调试接口;软件方面,引导程序和Linux内核,设备驱动程序形成基本的嵌入式运行环境,应用层负责视频采集,压缩及传输。

2 软件设计
    软件设计包括嵌入式系统构建和应用软件两部分,其中嵌入式系统部分按照华恒科技S3C6410用户手册构建,其中包括了Bootloader,Linux内核,交叉编译器,驱动等;下面介绍应用软件设计:

    监控前端:
    监控前端主要包括视频采集模块和视频压缩编码模块。
    视频采集模块使用V4L2接口收集摄像头数据到缓冲区中,视频压缩模块调用MFC驱动把YUV420数据压缩编码,同时可以指定编码参数。示例代码如下:
   
cam_fp = open(cam_name, O_RDWR);        //打开camera设备
    ...
    mfc_fd = open(MFC_DEV_NAME, O_RDWR|O_NDELAY);    //打开MFC设备
    ...
    addr = (char *) mmap(0,BUF_SIZE,PROT_READ | PROT_WRITE,MAP_SHARED,mfc_fd,0);        //mmap MFC
    //设置编码参数
    enc_init.in_width        =    out_width;
    enc_init.in_height        =    out_height;
    enc_init.in_frameRateRes    =    atoi(argv[2]);
    enc_init.in_frameRateDiv    =    0;
    enc_init.in_bitrate         =    atoi(argv[3]);
    enc_init.in_gopNum        =    atoi(argv[4]);
   
    ioctl(mfc_fd, IOCTL_MFC_H264_ENC_INIT, &enc_init);

    frame_size  = (enc_init.in_width * enc_init.in_height * 3) >> 1;
    //得到MFC输入缓冲区地址
    get_buf_addr.in_usr_data = (int)addr;
    ioctl(mfc_fd, IOCTL_MFC_GET_FRAM_BUF_ADDR, &get_buf_addr);
    in_buf = (char *)get_buf_addr.out_buf_addr;
    //得到MFC输出缓冲区地址
    get_buf_addr.in_usr_data = (int)addr;
    ioctl(mfc_fd, IOCTL_MFC_GET_LINE_BUF_ADDR, &get_buf_addr);
    out_buf = (char *)get_buf_addr.out_buf_addr;
    //V4L2编程
    ioctl(cam_fp, VIDIOC_QUERYCAP, &cap);
    //选择输入/输出
    ioctl(cam_fp, VIDIOC_S_INPUT, &index);
    ioctl(cam_fp, VIDIOC_S_OUTPUT, &index);
    //设置格式,注意必须设置输出为YUV420格式
    ioctl(cam_fp, VIDIOC_G_FBUF, &fb);
    fb.capability = cap.capabilities;
    fb.fmt.width = out_width;
    fb.fmt.height = out_height;
    fb.fmt.pixelformat = V4L2_PIX_FMT_YUV420;
    ioctl(cam_fp, VIDIOC_S_FBUF, &fb);
   
    on = 1;
    ioctl(cam_fp, VIDIOC_OVERLAY, &on);

    while(1)
    {
        ...
        //接收摄像头数据到MFC的输入缓冲区
        read(cam_fp,  in_buf,  (out_width * out_height * 3 / 2));
        //控制MFC开始编码
        ioctl(mfc_fd, IOCTL_MFC_H264_ENC_EXE, &enc_exe);
        //把编码后帧的大小及数据发送出去
        send(net_fd, &enc_exe.out_encoded_size, 2, 0);
        send(net_fd, out_buf, enc_exe.out_encoded_size, 0);
        ...
    }
把摄像头的输出直接设为MFC的输入可以节省一次内存操作。

监控终端:
监控终端是一台运行Linux的PC机,主要负责从网络接收压缩包,使用SDL和Avcodec解码并显示,示例代码如下:
...
While(1)
{
    ...
    //接收压缩包
    recv(sock_fd, &size, 2, MSG_WAITALL);
    recv(sock_fd, &frame_buf[0], size, MSG_WAITALL);
    //初始化avcodec
    if (!pCodecCtx)
    {
        avcodec_init();
        pCodecCtx = avcodec_alloc_context();
        avcodec_open(pCodecCtx, pCodec);
        //设置参数
        if (pCodec->capabilities & CODEC_CAP_TRUNCATED)
        {
            pCodecCtx->flags |=   CODEC_FLAG_TRUNCATED;
            pCodecCtx->height = 480;
            pCodecCtx->width  = 640;
        }
        pFrame = avcodec_alloc_frame();
        avcodec_decode_video(pCodecCtx, pFrame, &frame_finishd, sps_buf, sizeof(sps_buf));
        //初始化SDL
        SDL_Init(SDL_INIT_VIDEO);
        screen = SDL_SetVideoMode (pCodecCtx->width, pCodecCtx->height, 0, 0);
        yuv = SDL_CreateYUVOverlay(pCodecCtx->width, pCodecCtx->height, SDL_YV12_OVERLAY, screen);
       
        SDL_WM_SetCaption(buf, "H264/TCP");
        rect.y = rect.x = 0;
        rect.w = pCodecCtx->width;
        rect.h = pCodecCtx->height;

        pict.data[0]     = yuv->pixels[0];
        pict.data[1]     = yuv->pixels[2];
        pict.data[2]     = yuv->pixels[1];

        pict.linesize[0] = yuv->pitches[0];
        pict.linesize[1] = yuv->pitches[2];
        pict.linesize[2] = yuv->pitches[1];
    }

    if (pCodecCtx)
    {
        //解码视频流
        avcodec_decode_video(pCodecCtx, pFrame, &frame_finishd, frame_buf, size);
        if (frame_finishd && yuv)
        {
            SDL_LockYUVOverlay(yuv);
            if ((!swsctx && !(swsctx = sws_getContext(pCodecCtx->width,
                pCodecCtx->height, pCodecCtx->pix_fmt,
                pCodecCtx->width,  pCodecCtx->height,
                PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL))) ||
            sws_scale(swsctx, pFrame->data, pFrame->linesize, 0,
                pCodecCtx->height, pict.data, pict.linesize))
                    perror("sws ");
           
            SDL_UnlockYUVOverlay (yuv);
            //显示
            SDL_DisplayYUVOverlay(yuv, &rect);
        }
    }
}
以上程序编译需要libSDL, libavcodec, libswscale。

3 小结
本系统基于S3C6410实时采集,编码,传输数据,基本实现了视频监控的功能,测试采集,编码,传输VGA的图像可以达到25fps,使用多线程优化后应该可以更高,VGA分辨率图像经过H.264编码后平均每帧只有12k左右,占用网络带宽很少。
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
您好!以下是一个使用FFmpeg进行音频解码的C语言例程: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libavutil/frame.h> int main(int argc, char *argv[]) { AVFormatContext *formatContext = NULL; AVCodecContext *codecContext = NULL; AVCodec *codec = NULL; AVFrame *frame = NULL; AVPacket packet; int audioStreamIndex, ret; // 初始化FFmpeg av_register_all(); // 打开输入文件 if (avformat_open_input(&formatContext, argv[1], NULL, NULL) != 0) { fprintf(stderr, "无法打开输入文件\n"); return -1; } // 查找音频流信息 if (avformat_find_stream_info(formatContext, NULL) < 0) { fprintf(stderr, "无法获取流信息\n"); avformat_close_input(&formatContext); return -1; } // 查找音频流 audioStreamIndex = -1; for (int i = 0; i < formatContext->nb_streams; i++) { if (formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { audioStreamIndex = i; break; } } if (audioStreamIndex == -1) { fprintf(stderr, "未找到音频流\n"); avformat_close_input(&formatContext); return -1; } // 获取解码器参数 codecContext = avcodec_alloc_context3(NULL); if (!codecContext) { fprintf(stderr, "无法分配解码器上下文\n"); avformat_close_input(&formatContext); return -1; } if (avcodec_parameters_to_context(codecContext, formatContext->streams[audioStreamIndex]->codecpar) < 0) { fprintf(stderr, "无法获取解码器参数\n"); avcodec_free_context(&codecContext); avformat_close_input(&formatContext); return -1; } // 查找解码器 codec = avcodec_find_decoder(codecContext->codec_id); if (codec == NULL) { fprintf(stderr, "未找到解码器\n"); avcodec_free_context(&codecContext); avformat_close_input(&formatContext); return -1; } // 打开解码器 if (avcodec_open2(codecContext, codec, NULL) < 0) { fprintf(stderr, "无法打开解码器\n"); avcodec_free_context(&codecContext); avformat_close_input(&formatContext); return -1; } // 分配帧内存 frame = av_frame_alloc(); if (!frame) { fprintf(stderr, "无法分配帧内存\n"); avcodec_free_context(&codecContext); avformat_close_input(&formatContext); return -1; } // 读取音频数据 while (av_read_frame(formatContext, &packet) >= 0) { if (packet.stream_index == audioStreamIndex) { ret = avcodec_send_packet(codecContext, &packet); if (ret < 0) { fprintf(stderr, "解码错误: %d\n", ret); break; } while (ret >= 0) { ret = avcodec_receive_frame(codecContext, frame); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { break; } else if (ret < 0) { fprintf(stderr, "解码错误: %d\n", ret); break; } // 在这里处理解码后的音频帧数据 // frame->data[0], frame->data[1], ... 分别是各个声道的音频数据 // frame->nb_samples 是每个声道的采样数 } av_packet_unref(&packet); } } // 释放资源 av_frame_free(&frame); avcodec_free_context(&codecContext); avformat_close_input(&formatContext); return 0; } ``` 请注意,在使用此例程之前,您需要确保已正确安装了FFmpeg库,并将其正确链接到您的项目中。此外,您需要将音频文件的路径作为命令行参数传递给该程序。 希望这能帮助到您!如果您还有其他问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值