【问题标题】:Javacv: Decoding H.264 "live" stream coming from red5 server on android deviceJavacv:解码来自Android设备上red5服务器的H.264“实时”流
【发布时间】:2015-03-26 17:19:53
【问题描述】:

这是我的问题, 我已经使用 Red5 实现了一个服务器端应用程序,它发送 H.264 编码的实时流,在客户端接收流作为字节 []
为了在 Android 客户端解码它,我遵循了 Javacv-FFmpeg 库。解码代码如下

public Frame decodeVideo(byte[] data,long timestamp){
           frame.image = null;
           frame.samples = null;
           avcodec.av_init_packet(pkt);
           BytePointer video_data = new BytePointer(data);
           avcodec.AVCodec codec = avcodec.avcodec_find_decoder(codec_id);
           video_c = null;
           video_c = avcodec.avcodec_alloc_context3(codec);
           video_c.width(320);
           video_c.height(240);
           video_c.pix_fmt(0);
           video_c.flags2(video_c.flags2()|avcodec.CODEC_FLAG2_CHUNKS);
           avcodec.avcodec_open2(video_c, codec, null))
           picture = avcodec.avcodec_alloc_frame()
           pkt.data(video_data);
           pkt.size(data.length);
           int len = avcodec.avcodec_decode_video2(video_c, picture, got_frame, pkt);
           if ((len >= 0) && ( got_frame[0] != 0)) {
             ....
              process the decoded frame into **IPLImage of Javacv** and render it with **Imageview** of Android
           }
} 

从服务器收到的数据如下
少数帧具有以下模式
17 01 00 00 00 00 00 00 02 09 10 00 00 00 0F 06 00 01 C0 01 07 09 08 04 9A 00 00 03 00 80 00 00 16 EF 65 88 80 07 0 05 6C 98 90 00...

许多帧具有以下模式
27 01 00 00 00 00 00 00 02 09 30 00 00 00 0C 06 01 07 09 08 05 9A 00 00 03 00 80 00 00 0D 77 41 9A 02 04 15 B5 06 20 E3 11 E2 3C 46 ....

对于解码器使用 H.264 编解码器,解码器输出长度 >0 但 got_frames=0 始终。
使用 MPEG1 编解码器,解码器输出长度 >0 且 got_frames>0 但输出图像为绿色或失真。

但是,按照 javacv 的 FFmpegFrameGrabber 代码,我可以使用与上述类似的代码解码本地文件(H.264 编码)。

我想知道我缺少哪些细节,以及与标头相关的数据操作或设置适合解码器的编解码器。

任何建议,感谢您的帮助。
提前致谢。

【问题讨论】:

    标签: android ffmpeg red5 javacv live-streaming


    【解决方案1】:

    Atlast... 经过大量 RnD 后终于开始工作了。
    我缺少的是分析视频帧结构。视频由“I”、“P”帧组成。“I”帧是信息帧,它存储有关下一个后续帧的信息。 “P”帧是图片帧,它包含实际的视频帧...
    所以我需要解码“I”帧中的“P”帧w.r.t信息.. 所以最终的代码是这样的

    public IplImage decodeFromVideo(byte[] data, long timeStamp) {
    avcodec.av_init_packet(reveivedVideoPacket); // Empty AVPacket
    /*
     * Determine if the frame is a Data Frame or Key. IFrame 1 = PFrame 0 = Key
     * Frame
     */
    byte frameFlag = data[1];
    byte[] subData = Arrays.copyOfRange(data, 5, data.length);
    
    BytePointer videoData = new BytePointer(subData);
    if (frameFlag == 0) {
        avcodec.AVCodec codec = avcodec
                .avcodec_find_decoder(avcodec.AV_CODEC_ID_H264);
        if (codec != null) {
            videoCodecContext = null;
            videoCodecContext = avcodec.avcodec_alloc_context3(codec);
            videoCodecContext.width(320);
            videoCodecContext.height(240);
            videoCodecContext.pix_fmt(avutil.AV_PIX_FMT_YUV420P);
            videoCodecContext.codec_type(avutil.AVMEDIA_TYPE_VIDEO);
            videoCodecContext.extradata(videoData);
            videoCodecContext.extradata_size(videoData.capacity());
    
            videoCodecContext.flags2(videoCodecContext.flags2()
                    | avcodec.CODEC_FLAG2_CHUNKS);
            avcodec.avcodec_open2(videoCodecContext, codec,
                    (PointerPointer) null);
    
            if ((videoCodecContext.time_base().num() > 1000)
                    && (videoCodecContext.time_base().den() == 1)) {
                videoCodecContext.time_base().den(1000);
            }
        } else {
            Log.e("test", "Codec could not be opened");
        }
    }
    
    if ((decodedPicture = avcodec.avcodec_alloc_frame()) != null) {
        if ((processedPicture = avcodec.avcodec_alloc_frame()) != null) {
            int width = getImageWidth() > 0 ? getImageWidth()
                    : videoCodecContext.width();
            int height = getImageHeight() > 0 ? getImageHeight()
                    : videoCodecContext.height();
    
            switch (imageMode) {
            case COLOR:
            case GRAY:
                int fmt = 3;
                int size = avcodec.avpicture_get_size(fmt, width, height);
                processPictureBuffer = new BytePointer(
                        avutil.av_malloc(size));
                avcodec.avpicture_fill(new AVPicture(processedPicture),
                        processPictureBuffer, fmt, width, height);
                returnImageFrame = opencv_core.IplImage.createHeader(320,
                        240, 8, 1);
                break;
            case RAW:
                processPictureBuffer = null;
                returnImageFrame = opencv_core.IplImage.createHeader(320,
                        240, 8, 1);
                break;
            default:
                Log.d("showit",
                        "At default of swith case 1.$SwitchMap$com$googlecode$javacv$FrameGrabber$ImageMode[ imageMode.ordinal()]");
            }
    
            reveivedVideoPacket.data(videoData);
            reveivedVideoPacket.size(videoData.capacity());
    
            reveivedVideoPacket.pts(timeStamp);
            videoCodecContext.pix_fmt(avutil.AV_PIX_FMT_YUV420P);
            decodedFrameLength = avcodec.avcodec_decode_video2(videoCodecContext,
                    decodedPicture, isVideoDecoded, reveivedVideoPacket);
    
    if ((decodedFrameLength >= 0) && (isVideoDecoded[0] != 0)) {
     .... Process image same as javacv .....
    }
    

    希望对其他人有所帮助..

    【讨论】:

    • 我认为这是我的问题的答案,但我不明白这一点:我
    • 嗨@Ichigo Kurosaki,对于JavaSE 上完全相同的功能,它是否同样适用?代码看起来如何?请问你有什么想法吗?
    猜你喜欢
    • 1970-01-01
    • 2014-03-18
    • 2011-08-18
    • 2015-09-05
    • 1970-01-01
    • 1970-01-01
    • 2014-01-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多