我在解码 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, 0, read);
}
if (checkFrames >= 10000){
processFrames(out.toByteArray());
out.reset();
checkFrames = 0;
}
}
out.close();
}
这是我收到的帧:
- [ 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 376 329 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]
- [0 0 0 1 65 154 17 4 43 254 167 132 0 0 3 0 3 0 3 0 3 0 3 0 3 0 3 3 75 194 241 40 121 173 242 242 242 242 242 250 5 232 246 119 186 165 226 165 226 214 105 130 124 105 130 124 150 124 150 166 166 166 166 176 176 176 176 222 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 60 4 138 73 149 61 155 29 31 4 116 234 48 197 0 0 3 0 3 0 227 14 18 114 18 114 0 166 128 0 166 128 0 12 224 0 1 1177 0 1 117 0 0 58 128 0 58 128 0 58 128 0 58 128 0 58 128 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 941]
- [0 0 0 1 65 154 33 4 43 254 167 132 0 0 3 0 3 0 3 0 0 3 0 0 3 0 3 75 199 167 167 157 255 212 255 21 243 68 18 228 228 133 189 253 161 160 46 160 46 152 253 126 253 126 254 64 64 64 64 77 214 214 214 214 214 214 214 214 214 215 211 188 241 138 70 122 224 198 10 180 220 72 72 73 229 191 109 180 242 159 155 155 155 166 157 160 167 167 127 127 227 227 227 4 240 50 50 194 50 194 198 236 0 0 3 0 3 0 3 0 5 200 0 5 200 0 0 0 0 0 0 0 234 0 239 64 0 39 64 0 39 64 0 7 136 7 136 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]
- [0 0 0 1 65 154 49 4 43 254 167 132 0 0 3 0 3 0 3 0 0 3 0 0 3 0 3 3 75 190 67 197 160 41 174 120 242 242 242 241 153 255 255 215 109 82 241 107 52 193 52 193 62 193 62 75 83 83 83 88 111 126 137 34 245 240 31 16 136 11 36 60 69 58 176 201 178 91 191 68 188 188 92 254 5 218 35 222 138 247 207 207 207 137 10 79 221 2321 23 21 21 109 181 109 181 136 46 120 57 1347 1347 13 194 13 194 13 194 0 49 96 1 254 96 1 254 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 238 248 0 254 76 0 133 160 0 83 0 83 0 57 224 0 57 224 0 53 32 32 0 62 32 32 0 81 64 0 176 128 1 1770 1770 0 177 0 177 0 177 0 177 0 177 0 0 4 152 0 17 16 0 85 0 1 243 0 12 144 0 114 89 129]
- [ 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 0 232 8 270 220 1 232 8 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 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 0 0 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 119167 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, 0, thirdNaluSize);
System.arraycopy(data, firstStartCodeIndex + 4, firstNalu, 0, firstNaluSize - 4);
System.arraycopy(data, secondStartCodeIndex + 4, secondNalu, 0, secondNaluSize - 4);
}
else {
firstNalu = new byte[firstNaluSize];
secondNalu = new byte[secondNaluSize];
thirdNalu = new byte[thirdNaluSize];
System.arraycopy(data, thirdStartCodeIndex, thirdNalu, 0, thirdNaluSize);
System.arraycopy(data, firstStartCodeIndex, firstNalu, 0, firstNaluSize);
System.arraycopy(data, secondStartCodeIndex, secondNalu, 0, 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, 0, frame.length, 0, 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制作的解码器对其进行修改。
有人可以帮我理解如何解码吗?(我愿意接受从我的电脑接收和播放流的新方法)谢谢