解码 mainprofile h264 帧流 Android

问题描述

我在解码 h264 帧时遇到问题。我正在通过 tcp 接收帧(我知道 udp 会更好,但我认为这不是问题)从我的电脑,h264 的级别是主要配置文件,并将其显示在我的 android 智能手机上。流为 1280x720。

public class H264Provider implements Runnable{

private byte[] sps_params = {0x00};
private byte[] pps_params = {0x00};
private byte[] i_frame = {0x00};

ServerSocket ss;
Socket s;

@Override
public void run() {


    try {
        ss = new ServerSocket(7801);
    } catch (IOException e) {
        e.printstacktrace();
    }

    while (true) {

        try {
            s = ss.accept();

            toByteArray(new DataInputStream(s.getInputStream()));

        }catch (IOException e){
            try {
                s.close();
            } catch (IOException ioException) {
                ioException.printstacktrace();
            }
            e.printstacktrace();
        }

    }
}

private void toByteArray(DataInputStream in) throws IOException {
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    int read = 0;
    int checkFrames = 0;
    byte[] buffer = new byte[2048];
    while (read != -1) {

        read = in.read(buffer);
        checkFrames += read;
        if (read != -1) {

            out.write(buffer,read);

        }
        if (checkFrames >= 10000){

            processFrames(out.toByteArray());
            out.reset();
            checkFrames = 0;
        }

    }
    out.close();

}

这是我收到的帧:

  1. [ 0 0 0 1 101 184 37 255 254 195 219 84 167 139 253 126 67 184 139 84 33 74 112 0 0 3 0 0 3 0 0 3 0 0 3 1 93 68 88 125 52 0 0 3 1 129 0 3 76 0 10 232 0 35 96 0 157 0 3 96 0 17 192 0 95 0 2 240 0 17 0 0 136 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 2 2]
  2. [ 0 0 0 1 65 154 17 4 43 254 167 132 0 0 3 0 0 3 0 0 3 0 0 3 3 75 194 241 40 121 173 22 242 250 5 232 246 119 186 165 226 214 105 130 124 150 166 176 222 249 232 186 171 158 167 186 37 201 119 63 249 101 164 225 213 28 185 165 15 247 100 32 63 209 61 50 129 254 90 137 105 132 29 185 202 12 32 229 158 23 138 189 39 230 169 197 208 74 0 65 64 200 182 233 162 252 184 56 186 60 239 30 60 4 138 73 149 61 155 29 31 4 116 234 48 197 0 0 3 0 227 14 18 114 0 0 166 128 0 12 224 0 1 117 0 0 58 128 0 9 208 0 1 226 0 0 105 128 0 29 64 0 7 72 0 2 40 0 0 159 0 0 62 192 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 9 41]
  3. [ 0 0 0 1 65 154 33 4 43 254 167 132 0 0 3 0 0 3 0 0 3 0 0 3 3 75 199 167 157 212 255 21 243 68 18 228 133 189 253 161 160 46 152 253 126 254 64 77 214 215 211 188 241 138 70 122 224 198 10 180 220 72 73 229 191 109 180 242 159 140 155 166 157 160 167 127 227 4 240 50 194 198 236 0 0 3 0 5 200 0 0 234 0 0 39 64 0 7 136 0 1 166 0 0 117 0 0 29 32 0 8 160 0 2 124 0 0 251 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 3 0 0 9 184]
  4. [ 0 0 0 1 65 154 49 4 43 254 167 132 0 0 3 0 0 3 0 0 3 0 0 3 3 75 190 67 197 160 41 174 120 242 241 153 255 215 109 82 241 107 52 193 62 75 83 88 111 126 137 34 245 240 31 16 136 11 36 60 69 58 176 201 178 91 191 68 188 92 254 5 218 35 222 138 247 207 137 10 79 221 23 21 21 109 181 136 46 120 57 147 13 194 0 49 96 1 254 183 95 16 0 218 255 0 13 179 144 1 110 210 0 52 35 0 10 221 48 2 238 248 0 254 76 0 133 160 0 83 0 0 57 224 0 53 32 0 62 32 0 81 64 0 176 128 1 177 0 4 152 0 17 16 0 85 0 1 243 0 12 144 0 114 89 129]
  5. [ 0 0 0 1 65 155 1 4 43 254 167 132 0 0 3 0 0 3 0 0 3 0 0 3 3 75 153 91 143 188 69 187 41 76 149 169 31 253 221 37 190 220 231 142 205 1 238 27 200 102 107 205 25 148 156 198 65 222 128 45 143 59 209 255 244 162 70 24 255 72 88 21 170 238 206 194 145 1 90 126 117 60 232 176 71 239 64 21 68 165 0 2 148 229 49 39 56 0 198 74 0 211 59 143 240 2 216 60 130 245 37 59 44 232 247 56 229 176 25 131 171 8 215 91 172 0 103 1 165 9 64 71 130 222 47 35 216 119 179] 104 [ 0 0 0 1 65 155 17 4 43 254 167 132 0 0 3 0 0 3 0 0 3 0 0 3 3 75 190 122 165 86 136 78 249 48 41 252 79 117 154 96 159 37 169 172 55 190 122 46 170 231 169 238 137 114 94 14 43 252 140 23 97 29 0 0 3 0 0 3 3 53 190 7 239 180 31 119 167 128 67 244 16 102 179 32 65 175 255 18 231 67 28 51 228 84 147 153 70 98 133 128 75 153 144 97]

我的问题是我找不到第 5 个字节(分别为 103 或 104)的 sps 和 pps nalu。我在缓冲区中存储 10000 个字节,然后将其传递给我的函数以查找 sps、pps 和 iframe:

private void processFrames(byte[] inStream){

    int firstStartCodeIndex = 6;
    int secondStartCodeIndex = 0;
    int thirdStartCodeIndex = 0;

    byte[] data = inStream;

    print(data);

    for (int i = 0; i < data.length-5; i++)
    {
        if (data[i] == 0x00 && data[i+1] == 0x00 && data[i+2] == 0x00 && data[i+3] == 0x01)
        {

            if ((data[i + 4] & 0x1F) == 7) //SPS
            {
                Log.i("sps","true");
                firstStartCodeIndex = i;
                break;
            }
        }
    }

    int firstNaluSize = 0;

    for (int i = firstStartCodeIndex + 4; i < data.length-5; i++)
    {
        if (data[i] == 0x00 && data[i+1] == 0x00 && data[i+2] == 0x00 && data[i+3] == 0x01)
        {
            if (firstNaluSize == 0)
            {
                Log.i("fnalusize","0");
                firstNaluSize = i - firstStartCodeIndex;
            }
            if ((data[i + 4] & 0x1F) == 8) //PPS
            {
                Log.i("pps","true");
                secondStartCodeIndex = i;
                break;
            }
        }
    }

    int secondNaluSize = 0;

    for (int i = secondStartCodeIndex + 4; i < data.length-5; i++)
    {

        if (data[i] == 0x00 && data[i+1] == 0x00 && data[i+2] == 0x00 && data[i+3] == 0x01)
        {
            if (secondNaluSize == 0)
            {
                Log.i("secnalusize","0");
                secondNaluSize = i - secondStartCodeIndex;
            }
            if ((data[i+4] & 0x1F) == 5 || (data[i+4] & 0x1F) == 1) //IFrame or PFrame
            {
                Log.i("IFrame","true");
                thirdStartCodeIndex = i;
                break;
            }
        }
    }

    int thirdNaluSize = 0;
    int counter = thirdStartCodeIndex + 4;
    while (counter++ < data.length - 1)
    {
        if (data[counter] == 0x00 && data[counter + 1] == 0x00 && data[counter + 2] == 0x00 && data[counter + 3] == 0x01)
        {
            thirdNaluSize = counter - thirdStartCodeIndex;
            break;
        }
    }

    byte[] firstNalu;
    byte[] secondNalu;
    byte[] thirdNalu;
    // This is how you would remove the "\x00\x00\x00\x01"

    if (firstNaluSize != 0 && secondNaluSize != 0 && thirdNaluSize != 0) {
        firstNalu = new byte[firstNaluSize - 4];
        secondNalu = new byte[secondNaluSize - 4];
        thirdNalu = new byte[thirdNaluSize];

        System.arraycopy(data,thirdStartCodeIndex,thirdNalu,thirdNaluSize);
        System.arraycopy(data,firstStartCodeIndex + 4,firstNalu,firstNaluSize - 4);
        System.arraycopy(data,secondStartCodeIndex + 4,secondNalu,secondNaluSize - 4);
    }
    else {

        firstNalu = new byte[firstNaluSize];
        secondNalu = new byte[secondNaluSize];
        thirdNalu = new byte[thirdNaluSize];

        System.arraycopy(data,firstStartCodeIndex,firstNaluSize);
        System.arraycopy(data,secondStartCodeIndex,secondNaluSize);
    }

    sps_params = firstNalu;
    pps_params = secondNalu;
    i_frame = thirdNalu;

}

public byte[] getSPS () {
    return sps_params;
}

public byte[] getPPS () {
    return pps_params;
}

public byte[] nextFrame () {
    return i_frame;
}

public void release () throws IOException {
    s.close();
}

传递给我的媒体编解码器:

@Override
public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface,int width,int height) {

    // when the surface is ready,we make a H264 provider Object.  When its constructor
    // runs it starts an AsyncTask to log into our server and start getting frames
    // I have dummed down this demonstration to access the local h264 video from the raw resources dir
    provider = new H264Provider();

    new Thread(provider).start();

    //Create the format settings for the MediaCodec
    MediaFormat format = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC,this.width,this.height);
    // Set the SPS frame
    format.setByteBuffer("csd-0",ByteBuffer.wrap(provider.getSPS()));
    // Set the PPS frame
    format.setByteBuffer("csd-1",ByteBuffer.wrap(provider.getPPS()));
    // Set the buffer size
    format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE,this.width * this.height);

    try {
        // Get an instance of MediaCodec and give it its Mime type
        m_codec = MediaCodec.createDecoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);
        // Configure the codec
        m_codec.configure(format,new Surface(m_surface.getSurfaceTexture()),null,0);
        // Start the codec
        m_codec.start();
        // Create the AsyncTask to get the frames and decode them using the Codec
        m_fraMetask = new DecodeFramesTask(this);
        m_fraMetask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
    } catch (Exception e) {
        e.printstacktrace();
    }
}

解码帧任务:

public class DecodeFramesTask extends AsyncTask<String,String,String> {

SwarmControlActivity scActivity;

protected DecodeFramesTask(SwarmControlActivity scActivity){
    this.scActivity = scActivity;
}

@Override
protected String doInBackground(String... strings) {
    while (!isCancelled()) {
        // Get the next frame
        byte[] frame = this.scActivity.provider.nextFrame();

        // Now we need to give it to the Codec to decode into the surface

        // Get the input buffer from the decoder
        // Pass in -1 here as in this example we don't have a playback time reference
        int inputIndex = this.scActivity.m_codec.dequeueInputBuffer(1000);

        // If the buffer number is valid use the buffer with that index
        if (inputIndex >= 0) {

            ByteBuffer buffer = this.scActivity.m_codec.getInputBuffer(inputIndex);

            try {
                buffer.put(frame);
            }catch(NullPointerException e) {
                e.printstacktrace();
            }

            // Tell the decoder to process the frame
            this.scActivity.m_codec.queueInputBuffer(inputIndex,frame.length,0);
        }

        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
        int outputIndex = this.scActivity.m_codec.dequeueOutputBuffer(info,0);
        if (outputIndex >= 0) {
            // Output the image to our SufaceTexture
            this.scActivity.m_codec.releaSEOutputBuffer(outputIndex,true);
        }

        // wait for the next frame to be ready
        try {
            Thread.sleep(250);
        }catch (Exception e) {
            e.printstacktrace();
        }
    }
    return "";
}

@Override
protected void onPostExecute(String result) {
    try {
        this.scActivity.m_codec.stop();
        this.scActivity.m_codec.release();
    }catch (Exception e) {
        e.printstacktrace();
    }
    try {
        this.scActivity.provider.release();
    } catch (IOException e) {
        e.printstacktrace();
    }
}

}

我使用kirkify修改后的解码器。

有人可以帮我理解如何解码吗? (我对从我的电脑接收和播放流的新方法持开放态度) 谢谢

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...