H264推流拉流实现(硬编解码)

学习目标:

H264推流拉流实现(硬编解码)

学习内容:

当前实现的是在区域网内的H264编解码,在启动的时候我们需要先启动推流端(服务端),然后再启动拉流端(客户端)。本例采用的是webSocket的方式实现的手机投屏到另一个手机的功能,接下来上代码。
推流端
推流端项目代码链接
推流端的代码主要实现方式就是通过录屏将数据通过MediaCodec编码成H264编码推送出去。

下面就是推流的时候关键代码,具体代码请看上面项目链接。

//处理并且发送录屏的数据
    private void dealFrame(ByteBuffer bb, MediaCodec.BufferInfo bufferInfo) {
        //跳过分隔符
        int offset = 4;
        if (bb.get(2) == 0x01) {
            offset = 3;
        }
        //当前帧类型
        int type = bb.get(offset) & 0x1F;
        if (type == NAL_SPS) {
            vps_sps_pps_buf = new byte[bufferInfo.size];
            bb.get(vps_sps_pps_buf);
        } else if (type == NAL_I) {
            final byte[] bytes = new byte[bufferInfo.size];
            bb.get(bytes);
            byte[] newBuf = new byte[vps_sps_pps_buf.length + bytes.length];
            System.arraycopy(vps_sps_pps_buf, 0, newBuf, 0, vps_sps_pps_buf.length);
            System.arraycopy(bytes, 0, newBuf, vps_sps_pps_buf.length, bytes.length);
            this.socketLive.sendData(newBuf);
        } else {
            final byte[] bytes = new byte[bufferInfo.size];
            bb.get(bytes);
            this.socketLive.sendData(bytes);
        }

//        以下是当推流H265的时候,代码示例如下:
//        public static final int NAL_I = 19;
//        public static final int NAL_VPS = 32;
//        private byte[] vps_sps_pps_buf;
//        int type = (bb.get(offset) & 0x7E) >> 1;
//        if (type == NAL_VPS) {
//            vps_sps_pps_buf = new byte[bufferInfo.size];
//            bb.get(vps_sps_pps_buf);
//        } else if (type == NAL_I) {
//            final byte[] bytes = new byte[bufferInfo.size];
//            bb.get(bytes);
//            byte[] newBuf = new byte[vps_sps_pps_buf.length + bytes.length];
//            System.arraycopy(vps_sps_pps_buf, 0, newBuf, 0, vps_sps_pps_buf.length);
//            System.arraycopy(bytes, 0, newBuf, vps_sps_pps_buf.length, bytes.length);
//            this.socketLive.sendData(newBuf);
//        } else {
//            final byte[] bytes = new byte[bufferInfo.size];
//            bb.get(bytes);
//            this.socketLive.sendData(bytes);
//        }

    }

大家可以看到,上面的代码的注释部分是对于H265的进行处理的,博主之所以注释掉,是因为我第一次的时候本来是想推送H265的,没想到手机不争气(不支持h265),所以勉为其难就写了推送H264的。
拉流端
拉流端项目代码链接
推流端就是把推送过来的数据通过MediaCodec解析出来渲染到surfaceview上。下面就是推流的时候关键代码,具体代码请看上面项目链接。

public void callBack(byte[] data) {
        Log.i(TAG, "接收到消息: " + data.length);
        int index = mediaCodec.dequeueInputBuffer(100000);
        //索引
        if (index >= 0) {
            ByteBuffer inputBuffer = mediaCodec.getInputBuffer(index);
            inputBuffer.clear();
            inputBuffer.put(data, 0, data.length);
            //通知dsp芯片解码
            mediaCodec.queueInputBuffer(index, 0, data.length, System.currentTimeMillis(), 0);
        }

        //获取数据
        MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
        int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 100000);

        while (outputBufferIndex > 0) {
            mediaCodec.releaseOutputBuffer(outputBufferIndex, true);
            outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);
        }
    }

ps: 运行项目时,ip地址根据服务器的地址做调整,并且推流拉流端的port也需要在每次运行的时候重新更换一个新的端口号(会出现端口号占用)。最后,一定要先运行推流端,在运行拉流端

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你可以使用FFmpeg来取H264视频并在浏览器中播放。以下是一些步骤和方法: 1.使用FFmpeg的avformat_open_input()函数打开视频。 2.使用avformat_find_stream_info()函数查找视频的信息。 3.使用avcodec_find_decoder()函数查找解码器。 4.使用avcodec_open2()函数打开解码器。 5.使用av_read_frame()函数读取视频帧。 6.使用avcodec_decode_video2()函数解码视频帧。 7.使用sws_scale()函数将解码后的视频帧转换为RGB格式。 8.使用SDL库或其他库将RGB格式的视频帧显示在屏幕上。 以下是一个简单的C语言程序,可以使用FFmpeg取H264视频并将其显示在屏幕上: <<引用:>> gcc VedioToH264_demo.c -o ffmpegtest -I /home/xy/ffmpeg/ffmpeg-4.0.2/out/include/ -L /home/xy/ffmpeg/ffmpeg-4.0.2/out/lib/ -lavformat -lavcodec -lavutil -lswresample -lm -pthread -lrt -ldl -lz -lX11 -lvdpau <<代码>> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <pthread.h> #include <SDL/SDL.h> #include <SDL/SDL_thread.h> #include <libavformat/avformat.h> #include <libswscale/swscale.h> #define SDL_AUDIO_BUFFER_SIZE 1024 #define MAX_AUDIO_FRAME_SIZE 192000 int quit = 0; void quit_signal(int signo) { quit = 1; } int main(int argc, char *argv[]) { AVFormatContext *pFormatCtx = NULL; int i, videoStream; AVCodecContext *pCodecCtxOrig = NULL; AVCodecContext *pCodecCtx = NULL; AVCodec *pCodec = NULL; AVFrame *pFrame = NULL; AVPacket packet; int frameFinished; struct SwsContext *sws_ctx = NULL; SDL_Surface *screen = NULL; SDL_Overlay *bmp = NULL; SDL_Rect rect; SDL_Event event; SDL_Thread *video_tid = NULL; int videoWidth, videoHeight; Uint32 pixformat; char *filename = "rtsp://192.168.1.100:554/av0_0"; int ret; signal(SIGINT, quit_signal); signal(SIGTERM, quit_signal); av_register_all(); avformat_network_init(); pFormatCtx = avformat_alloc_context(); if (avformat_open_input(&pFormatCtx, filename, NULL, NULL) != 0) { fprintf(stderr, "Cannot open input file\n"); return -1; } if (avformat_find_stream_info(pFormatCtx, NULL) < 0) { fprintf(stderr, "Cannot find stream information\n"); return -1; } videoStream = -1; for (i = 0; i < pFormatCtx->nb_streams; i++) { if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { videoStream = i; break; } } if (videoStream == -1) { fprintf(stderr, "Cannot find a video stream\n"); return -1; } pCodecCtxOrig = pFormatCtx->streams[videoStream]->codec; pCodec = avcodec_find_decoder(pCodecCtxOrig->codec_id); if (pCodec == NULL) { fprintf(stderr, "Unsupported codec\n"); return -1; } pCodecCtx = avcodec_alloc_context3(pCodec); if (avcodec_parameters_to_context(pCodecCtx, pFormatCtx->streams[videoStream]->codecpar) < 0) { fprintf(stderr, "Failed to copy codec parameters to decoder context\n"); return -1; } if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) { fprintf(stderr, "Failed to open codec\n"); return -1; } pFrame = av_frame_alloc(); screen = SDL_SetVideoMode(pCodecCtx->width, pCodecCtx->height, 0, 0); if (!screen) { fprintf(stderr, "SDL: could not set video mode - exiting\n"); exit(1); } bmp = SDL_CreateYUVOverlay(pCodecCtx->width, pCodecCtx->height, SDL_YV12_OVERLAY, screen); sws_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, PIX_FMT_YUV420P, SWS_BILINEAR, NULL, NULL, NULL); video_tid = SDL_CreateThread(video_thread, NULL); while (!quit) { SDL_WaitEvent(&event); switch (event.type) { case SDL_QUIT: quit = 1; break; default: break; } } SDL_Quit(); avcodec_close(pCodecCtx); av_free(pCodecCtx); avformat_close_input(&pFormatCtx); return 0; } int video_thread(void *arg) { AVFormatContext *pFormatCtx = NULL; int i, videoStream; AVCodecContext *pCodecCtxOrig = NULL; AVCodecContext *pCodecCtx = NULL; AVCodec *pCodec = NULL; AVFrame *pFrame = NULL; AVPacket packet; int frameFinished; struct SwsContext *sws_ctx = NULL; SDL_Surface *screen = NULL; SDL_Overlay *bmp = NULL; SDL_Rect rect; SDL_Event event; int videoWidth, videoHeight; Uint32 pixformat; char *filename = "rtsp://192.168.1.100:554/av0_0"; int ret; av_register_all(); avformat_network_init(); pFormatCtx = avformat_alloc_context(); if (avformat_open_input(&pFormatCtx, filename, NULL, NULL) != 0) { fprintf(stderr, "Cannot open input file\n"); return -1; } if (avformat_find_stream_info(pFormatCtx, NULL) < 0) { fprintf(stderr, "Cannot find stream information\n"); return -1; } videoStream = -1; for (i = 0; i < pFormatCtx->nb_streams; i++) { if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { videoStream = i; break; } } if (videoStream == -1) { fprintf(stderr, "Cannot find a video stream\n"); return -1; } pCodecCtxOrig = pFormatCtx->streams[videoStream]->codec; pCodec = avcodec_find_decoder(pCodecCtxOrig->codec_id); if (pCodec == NULL) { fprintf(stderr, "Unsupported codec\n"); return -1; } pCodecCtx = avcodec_alloc_context3(pCodec); if (avcodec_parameters_to_context(pCodecCtx, pFormatCtx->streams[videoStream]->codecpar) < 0) { fprintf(stderr, "Failed to copy codec parameters to decoder context\n"); return -1; } if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) { fprintf(stderr, "Failed to open codec\n"); return -1; } pFrame = av_frame_alloc(); screen = SDL_SetVideoMode(pCodecCtx->width, pCodecCtx->height, 0, 0); if (!screen) { fprintf(stderr, "SDL: could not set video mode - exiting\n"); exit(1); } bmp = SDL_CreateYUVOverlay(pCodecCtx->width, pCodecCtx->height, SDL_YV12_OVERLAY, screen); sws_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, PIX_FMT_YUV420P, SWS_BILINEAR, NULL, NULL, NULL); while (av_read_frame(pFormatCtx, &packet) >= 0) { if (packet.stream_index == videoStream) { ret = avcodec_send_packet(pCodecCtx, &packet); if (ret < 0) { fprintf(stderr, "Error sending a packet for decoding\n"); break; } while (ret >= 0) { ret = avcodec_receive_frame(pCodecCtx, pFrame); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { break; } else if (ret < 0) { fprintf(stderr, "Error during decoding\n"); break; } sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, bmp->pixels, bmp->pitches); rect.x = 0; rect.y = 0; rect.w = pCodecCtx->width; rect.h = pCodecCtx->height; SDL_DisplayYUVOverlay(bmp, &rect); } } av_packet_unref(&packet); if (quit) { break; } } avcodec_close(pCodecCtx); av_free(pCodecCtx); avformat_close_input(&pFormatCtx); return 0; }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值