Example #1
0
        public void StartPlay(NetworkStream stream)
        {
            try
            {
                AVCodec *mCodec = ffmpeg.avcodec_find_decoder(AVCodecID.AV_CODEC_ID_H264);
                if (mCodec == null)
                {
                    Console.WriteLine("can not find h264 decoder");
                    return;
                }

                AVCodecContext *mCodecContext = ffmpeg.avcodec_alloc_context3(mCodec);
                if (mCodecContext == null)
                {
                    Console.WriteLine("can not allcote codec context");
                    return;
                }

                if (ffmpeg.avcodec_open2(mCodecContext, mCodec, null) < 0)
                {
                    Console.WriteLine("can not open codec");
                    ffmpeg.avcodec_free_context(&mCodecContext);
                    return;
                }

                AVCodecParserContext *parser = ffmpeg.av_parser_init((int)AVCodecID.AV_CODEC_ID_H264);
                if (parser == null)
                {
                    Console.WriteLine("can not initialize parser");
                    ffmpeg.avcodec_close(mCodecContext);
                    ffmpeg.avcodec_free_context(&mCodecContext);
                    return;
                }

                //AVFrame用于存储解码后的像素数据(YUV)
                //内存分配
                AVFrame *pFrame = ffmpeg.av_frame_alloc();
                //YUV420
                AVFrame *pFrameYUV = ffmpeg.av_frame_alloc();
                //只有指定了AVFrame的像素格式、画面大小才能真正分配内存
                //缓冲区分配内存
                int   out_buffer_size = ffmpeg.avpicture_get_size(AVPixelFormat.AV_PIX_FMT_YUV420P, 720, 1280);
                byte *out_buffer      = (byte *)ffmpeg.av_malloc((ulong)out_buffer_size);
                //初始化缓冲区
                ffmpeg.avpicture_fill((AVPicture *)pFrameYUV, out_buffer, AVPixelFormat.AV_PIX_FMT_YUV420P, 720, 1280);

                //用于转码(缩放)的参数,转之前的宽高,转之后的宽高,格式等
                //SwsContext* sws_ctx = ffmpeg.sws_getContext(mCodecContext->width, mCodecContext->height, AVPixelFormat.AV_PIX_FMT_YUV420P /*pCodecCtx->pix_fmt*/, mCodecContext->width, mCodecContext->height, AVPixelFormat.AV_PIX_FMT_YUV420P, ffmpeg.SWS_BICUBIC, null, null, null);
                SwsContext *sws_ctx = ffmpeg.sws_getContext(720, 1280, AVPixelFormat.AV_PIX_FMT_YUV420P /*pCodecCtx->pix_fmt*/, 720, 1280, AVPixelFormat.AV_PIX_FMT_YUV420P, ffmpeg.SWS_BICUBIC, null, null, null);


                AVPacket packet;
                ffmpeg.av_init_packet(&packet);

                byte[] inbuffer = new byte[4096];
                int    cur_size, ret, got_picture;
                int    readLen    = 0;
                bool   first_time = true;

                sdlvideo.SDL_Init(720, 1280);

                while (true)
                {
                    readLen  = 0;
                    cur_size = stream.Read(inbuffer, 0, 4096);
                    if (cur_size <= 0)
                    {
                        Console.WriteLine("read eof");
                        break;
                    }

                    while (cur_size > 0)
                    {
                        fixed(byte *cur_ptr = inbuffer)
                        {
                            int len = ffmpeg.av_parser_parse2(parser, mCodecContext,
                                                              &packet.data, &packet.size, cur_ptr + readLen, cur_size,
                                                              ffmpeg.AV_NOPTS_VALUE, ffmpeg.AV_NOPTS_VALUE, -1);

                            readLen  += len;
                            cur_size -= len;
                        }

                        if (packet.size == 0)
                        {
                            continue;
                        }

                        Console.WriteLine($"[Packet]Size:{packet.size}");

                        ret = ffmpeg.avcodec_decode_video2(mCodecContext, pFrame, &got_picture, &packet);
                        if (ret < 0)
                        {
                            Console.WriteLine("Decode Error.\n");
                            break;
                        }
                        // 读取解码后的帧数据
                        if (got_picture > 0)
                        {
                            if (first_time)
                            {
                                Console.WriteLine($"width:{mCodecContext->width}\nheight:{mCodecContext->height}\n\n");
                                first_time = false;
                            }
                            //AVFrame转为像素格式YUV420,宽高
                            ffmpeg.sws_scale(sws_ctx, pFrame->data, pFrame->linesize, 0, mCodecContext->height, pFrameYUV->data, pFrameYUV->linesize);

                            //SDL播放YUV数据
                            var data = out_buffer;
                            sdlvideo.SDL_Display(mCodecContext->width, mCodecContext->height, (IntPtr)data, out_buffer_size, pFrameYUV->linesize[0]);
                        }
                    }
                }

                Console.WriteLine("exit loop");
                ffmpeg.av_parser_close(parser);
                ffmpeg.avcodec_close(mCodecContext);
                ffmpeg.avcodec_free_context(&mCodecContext);
                // todo notify stopped
            }
            catch (Exception ex)
            {
                Console.WriteLine("H264SocketParser.thread error:", ex);
            }
        }
        /// <summary>
        /// 视频H264转YUV并使用SDL进行播放
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="sdlVideo"></param>
        /// <returns></returns>
        public unsafe int RunVideo(string fileName, SDLHelper sdlVideo)
        {
            IsRun        = true;
            exit_thread  = false;
            pause_thread = false;
            threadVideo  = Thread.CurrentThread;
            int              error, frame_count = 0;
            int              got_picture, ret;
            SwsContext *     pSwsCtx  = null;
            AVFormatContext *ofmt_ctx = null;
            IntPtr           convertedFrameBufferPtr = IntPtr.Zero;

            try
            {
                // 注册编解码器
                ffmpeg.avcodec_register_all();

                // 获取文件信息上下文初始化
                ofmt_ctx = ffmpeg.avformat_alloc_context();

                // 打开媒体文件
                error = ffmpeg.avformat_open_input(&ofmt_ctx, fileName, null, null);
                if (error != 0)
                {
                    throw new ApplicationException($"ffmpeg avformat_open_input error {error}");
                }

                // 获取流的通道
                for (int i = 0; i < ofmt_ctx->nb_streams; i++)
                {
                    if (ofmt_ctx->streams[i]->codec->codec_type == AVMediaType.AVMEDIA_TYPE_VIDEO)
                    {
                        videoindex = i;
                        Console.WriteLine("video.............." + videoindex);
                    }
                }

                if (videoindex == -1)
                {
                    Console.WriteLine("Couldn't find a video stream.(没有找到视频流)");
                    return(-1);
                }

                // 视频流处理
                if (videoindex > -1)
                {
                    //获取视频流中的编解码上下文
                    AVCodecContext *pCodecCtx = ofmt_ctx->streams[videoindex]->codec;

                    //根据编解码上下文中的编码id查找对应的解码
                    AVCodec *pCodec = ffmpeg.avcodec_find_decoder(pCodecCtx->codec_id);
                    if (pCodec == null)
                    {
                        Console.WriteLine("没有找到编码器");
                        return(-1);
                    }

                    //打开编码器
                    if (ffmpeg.avcodec_open2(pCodecCtx, pCodec, null) < 0)
                    {
                        Console.WriteLine("编码器无法打开");
                        return(-1);
                    }
                    Console.WriteLine("Find a  video stream.channel=" + videoindex);

                    //输出视频信息
                    var format = ofmt_ctx->iformat->name->ToString();
                    var len    = (ofmt_ctx->duration) / 1000000;
                    var width  = pCodecCtx->width;
                    var height = pCodecCtx->height;
                    Console.WriteLine("video format:" + format);
                    Console.WriteLine("video length:" + len);
                    Console.WriteLine("video width&height:width=" + width + " height=" + height);
                    Console.WriteLine("video codec name:" + pCodec->name->ToString());

                    //准备读取
                    //AVPacket用于存储一帧一帧的压缩数据(H264)
                    //缓冲区,开辟空间
                    AVPacket *packet = (AVPacket *)ffmpeg.av_malloc((ulong)sizeof(AVPacket));

                    //AVFrame用于存储解码后的像素数据(YUV)
                    //内存分配
                    AVFrame *pFrame = ffmpeg.av_frame_alloc();
                    //YUV420
                    AVFrame *pFrameYUV = ffmpeg.av_frame_alloc();
                    //只有指定了AVFrame的像素格式、画面大小才能真正分配内存
                    //缓冲区分配内存
                    int   out_buffer_size = ffmpeg.avpicture_get_size(AVPixelFormat.AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height);
                    byte *out_buffer      = (byte *)ffmpeg.av_malloc((ulong)out_buffer_size);
                    //初始化缓冲区
                    ffmpeg.avpicture_fill((AVPicture *)pFrameYUV, out_buffer, AVPixelFormat.AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height);

                    //用于转码(缩放)的参数,转之前的宽高,转之后的宽高,格式等
                    SwsContext *sws_ctx = ffmpeg.sws_getContext(pCodecCtx->width, pCodecCtx->height, AVPixelFormat.AV_PIX_FMT_YUV420P /*pCodecCtx->pix_fmt*/, pCodecCtx->width, pCodecCtx->height, AVPixelFormat.AV_PIX_FMT_YUV420P, ffmpeg.SWS_BICUBIC, null, null, null);

                    // 初始化sdl
                    sdlVideo.SDL_Init(pCodecCtx->width, pCodecCtx->height);

                    while (ffmpeg.av_read_frame(ofmt_ctx, packet) >= 0)
                    {
                        // 退出线程
                        if (exit_thread)
                        {
                            break;
                        }
                        // 暂停解析
                        if (pause_thread)
                        {
                            while (pause_thread)
                            {
                                Thread.Sleep(100);
                            }
                        }
                        //只要视频压缩数据(根据流的索引位置判断)
                        if (packet->stream_index == videoindex)
                        {
                            //解码一帧视频压缩数据,得到视频像素数据
                            ret = ffmpeg.avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);
                            if (ret < 0)
                            {
                                Console.WriteLine("视频解码错误");
                                return(-1);
                            }

                            // 读取解码后的帧数据
                            if (got_picture > 0)
                            {
                                frame_count++;
                                Console.WriteLine("视频帧数:第 " + frame_count + " 帧");

                                //AVFrame转为像素格式YUV420,宽高
                                ffmpeg.sws_scale(sws_ctx, pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);

                                //SDL播放YUV数据
                                var data = out_buffer;
                                sdlVideo.SDL_Display(pCodecCtx->width, pCodecCtx->height, (IntPtr)data, out_buffer_size, pFrameYUV->linesize[0]);
                            }
                        }

                        //释放资源
                        ffmpeg.av_free_packet(packet);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
            finally
            {
                if (&ofmt_ctx != null)
                {
                    ffmpeg.avformat_close_input(&ofmt_ctx);//关闭流文件
                }
            }
            IsRun = false;
            return(0);
        }