MediaCode存在一些问题。
我有3个组件;解码器,下载器和渲染。简单FragmentStreamVideo
初始化' SurfaceView'和'#Downloader'。
渲染和解码器等其他组件在SurfaceView中初始化。然后,在Decoder和Dowloader之间进行同步,由BlockingQueue<String>
队列实现String = Filename
(每个帧都有其文件)。
Decode和Render之间的另一个同步是由文档中所述的标准ByteBuffer完成的。
在下面找到我的代码。如果你能提供帮助,我将非常感激。
SurfaceView
public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback {
private Render drawThread;
private Decoder decoder;
BlockingQueue<String> queue;
Context mContext;
public MySurfaceView(Context context,BlockingQueue<String> queue){
super(context);
this.queue = queue;
mContext = context;
getHolder().addCallback(this);
}
public void setRunning(boolean value){
drawThread.setRunning(value);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
//drawThread = new Render(getHolder().getSurface(),300,300);
drawThread = new Render(getHolder().getSurface(),352,288);
decoder = new Decoder(drawThread,queue,mContext);
decoder.start();
drawThread.setRunning(true);
drawThread.start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
// завершаем работу потока
drawThread.setRunning(false);
while (retry) {
try {
drawThread.join();
retry = false;
} catch (InterruptedException e) {
// если не получилось, то будем пытаться еще и еще
}
}
}
渲染
public class Render extends Thread{
private boolean mConfigured = false;
private long mTimeoutUs;
private MediaCodec mDecoder;
volatile boolean mRunning = false;
byte[] header_sps = { 0, 0, 0, 1, 103, 100, 0, 40, -84, 52, -59, 1, -32, 17, 31, 120, 11, 80, 16, 16, 31, 0, 0, 3, 3, -23, 0, 0, -22, 96, -108 };
byte[] header_pps = { 0, 0, 0, 1, 104, -18, 60, -128 };
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public Render(Surface surface, int width, int height){
mTimeoutUs = 1000l;
//возможны проблемы с инициализацией
ByteBuffer csd0 = ByteBuffer.allocate(2);
csd0.put((byte) MediaCodec.BUFFER_FLAG_CODEC_CONFIG);
try {
configure(surface,width,height,csd0);
} catch (IOException e) {
e.printStackTrace();
}
}
synchronized void configure(Surface surface, int width, int height,
ByteBuffer csd0) throws IOException {
if (mConfigured) { // просто флаг, чтобы знать, что декодер готов
throw new IllegalStateException();
}
// создаем видео формат
MediaFormat format = MediaFormat.createVideoFormat("video/avc", width, height);
// передаем наш csd-0
//format.setByteBuffer("csd-0", csd0);
//format.setByteBuffer("csd-0",ByteBuffer.wrap(header_sps));
format.setByteBuffer("csd-1", ByteBuffer.wrap(header_pps));
// создаем декодер
mDecoder = MediaCodec.createDecoderByType("video/avc");
// конфигурируем декодер
mDecoder.configure(format, surface, null, 0);
mDecoder.start();
mConfigured = true;
}
void decodeSample(byte[] data, int offset, int size, long presentationTimeUs, int flags) {
if (mConfigured) {
// вызов блокирующий
int index = mDecoder.dequeueInputBuffer(mTimeoutUs);
if (index >= 0) {
ByteBuffer buffer = mDecoder.getInputBuffers()[index];
buffer.clear(); // обязательно сбросить позицию и размер буфера
buffer.put(data, offset, size);
// сообщаем системе о доступности буфера данных
mDecoder.queueInputBuffer(index, 0, size, presentationTimeUs, flags);
}
}
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@Override
public void run() {
try {
MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); // переиспользуем BufferInfo
long startMs = System.currentTimeMillis();
while (mRunning) {
if (mConfigured) { // если кодек готов
int index = mDecoder.dequeueOutputBuffer(info, mTimeoutUs);
if (index >= 0) { // буфер с индексом index доступен
// info.size > 0: если буфер не нулевого размера, то рендерим на Surface
while (info.presentationTimeUs / 1000 > System.currentTimeMillis() - startMs) {
try {
sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
mDecoder.releaseOutputBuffer(index, info.size > 0);
// заканчиваем работу декодера если достигнут конец потока данных
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) == MediaCodec.BUFFER_FLAG_END_OF_STREAM) {
mRunning = false;
break;
}
}
} else {
// просто спим, т.к. кодек не готов
try {
Thread.sleep(10);
} catch (InterruptedException ignore) {
}
}
}
} finally {
// освобождение ресурсов
release();
}
}
public void setRunning(boolean value){
mRunning = value;
}
void release() {
if (mConfigured) {
mDecoder.stop();
mDecoder.release();
}
}
解码器
public class Render extends Thread{
private boolean mConfigured = false;
private long mTimeoutUs;
private MediaCodec mDecoder;
volatile boolean mRunning = false;
byte[] header_sps = { 0, 0, 0, 1, 103, 100, 0, 40, -84, 52, -59, 1, -32, 17, 31, 120, 11, 80, 16, 16, 31, 0, 0, 3, 3, -23, 0, 0, -22, 96, -108 };
byte[] header_pps = { 0, 0, 0, 1, 104, -18, 60, -128 };
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public Render(Surface surface, int width, int height){
mTimeoutUs = 1000l;
//возможны проблемы с инициализацией
ByteBuffer csd0 = ByteBuffer.allocate(2);
csd0.put((byte) MediaCodec.BUFFER_FLAG_CODEC_CONFIG);
try {
configure(surface,width,height,csd0);
} catch (IOException e) {
e.printStackTrace();
}
}
synchronized void configure(Surface surface, int width, int height,
ByteBuffer csd0) throws IOException {
if (mConfigured) { // просто флаг, чтобы знать, что декодер готов
throw new IllegalStateException();
}
// создаем видео формат
MediaFormat format = MediaFormat.createVideoFormat("video/avc", width, height);
// передаем наш csd-0
//format.setByteBuffer("csd-0", csd0);
//format.setByteBuffer("csd-0",ByteBuffer.wrap(header_sps));
format.setByteBuffer("csd-1", ByteBuffer.wrap(header_pps));
// создаем декодер
mDecoder = MediaCodec.createDecoderByType("video/avc");
// конфигурируем декодер
mDecoder.configure(format, surface, null, 0);
mDecoder.start();
mConfigured = true;
}
void decodeSample(byte[] data, int offset, int size, long presentationTimeUs, int flags) {
if (mConfigured) {
// вызов блокирующий
int index = mDecoder.dequeueInputBuffer(mTimeoutUs);
if (index >= 0) {
ByteBuffer buffer = mDecoder.getInputBuffers()[index];
buffer.clear(); // обязательно сбросить позицию и размер буфера
buffer.put(data, offset, size);
// сообщаем системе о доступности буфера данных
mDecoder.queueInputBuffer(index, 0, size, presentationTimeUs, flags);
}
}
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@Override
public void run() {
try {
MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); // переиспользуем BufferInfo
long startMs = System.currentTimeMillis();
while (mRunning) {
if (mConfigured) { // если кодек готов
int index = mDecoder.dequeueOutputBuffer(info, mTimeoutUs);
if (index >= 0) { // буфер с индексом index доступен
// info.size > 0: если буфер не нулевого размера, то рендерим на Surface
while (info.presentationTimeUs / 1000 > System.currentTimeMillis() - startMs) {
try {
sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
mDecoder.releaseOutputBuffer(index, info.size > 0);
// заканчиваем работу декодера если достигнут конец потока данных
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) == MediaCodec.BUFFER_FLAG_END_OF_STREAM) {
mRunning = false;
break;
}
}
} else {
// просто спим, т.к. кодек не готов
try {
Thread.sleep(10);
} catch (InterruptedException ignore) {
}
}
}
} finally {
// освобождение ресурсов
release();
}
}
public void setRunning(boolean value){
mRunning = value;
}
void release() {
if (mConfigured) {
mDecoder.stop();
mDecoder.release();
}
}
}
下载器(因为更多功能工作而不满)
在readFramePacket函数中我保存文件并将其放在队列中它可以正常工作。
public class Downloader extends Thread{
private final byte COMMAND_ONE = (byte)0x01;
private final byte COMMAND_TEN = (byte)0x10;
private final byte MAIN_TYPE_STREAM =(byte) 0x00;
private final byte SUPPORT_TYPE_STREAM =(byte) 0x01;
private final byte CONTROL_CODE_DISCONNECT =(byte) 0x00;
private final byte CONTROL_CODE_CONNECT =(byte) 0x01;
private final byte DELIMITER = (byte)':';
private final int FIX_PACKET_HEAD_SIZE = 5;
private final int SECOND_HEAD_FIX_SIZE = 4;
private final int FIX_DELIMITER_SIZE = 1;
private static final int HEAD_OF_PACKET = -1;
private static final int SECOND_HEAD_OF_PACKET = 0;
private static final int DATA_PACKET = 1;
private static final int PACKETS_END = -2;
private static final int LAST_PACKET = 8;
private static final int COMMAND_INDEX = 0;
private static final int SOCKET_TIMEOUT_LENGTH = 5000;
private String log;
private String pass;
String host;
int portNumber;
BlockingQueue<String> queue;
Socket socket;
Context mContext;
OutputStream out;
int cameraID;
int value = 0;
volatile boolean mRunning = false;
public Downloader(Server server,BlockingQueue<String> queue,int cameraID,Context context){
log = server.getLogin();
pass = server.getPassword();
host = server.getIP();
portNumber = Integer.parseInt(server.getPort());
this.queue = queue;
mRunning = true;
this.cameraID = cameraID;
mContext = context;
}
public void stopStreaming(){
mRunning = false;
}
@Override
public void run() {
try {
SocketAddress sockaddr = new InetSocketAddress(host, portNumber);
socket = new Socket();
socket.connect(sockaddr, SOCKET_TIMEOUT_LENGTH);
//socket.setSoTimeout(SOCKET_TIMEOUT_LENGTH);
InputStream inputStream = new BufferedInputStream(socket.getInputStream());
out = socket.getOutputStream();
byte[] command = createAuthenticationPacket(log, pass);
out.write(command);
readAuthPacket(inputStream);
byte[] connectCommand = packetToConnectDisconnectCamera(cameraID,SUPPORT_TYPE_STREAM,CONTROL_CODE_CONNECT);
out.write(connectCommand);
initializeVideoStream(inputStream);
readFramePacket(inputStream);
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchProviderException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}